PEL: Msg registry sev now based on sys type

The PEL severity field in the UserHeader section can now be defined in
the message registry to be based on system type.

When the UserHeader object is being created from the message registry,
it will now query the system type and use that to find the correct
severity value.

The Registry class was updated to handle the new JSON format, which
is an array of system type/severity value objects.

The original severity format of a simple string is still valid.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I0fe0ada7193c57adc42e82afdc30e687eea63eb9
diff --git a/extensions/openpower-pels/pel.cpp b/extensions/openpower-pels/pel.cpp
index c741727..2e675cb 100644
--- a/extensions/openpower-pels/pel.cpp
+++ b/extensions/openpower-pels/pel.cpp
@@ -50,7 +50,7 @@
 {
     _ph = std::make_unique<PrivateHeader>(regEntry.componentID, obmcLogID,
                                           timestamp);
-    _uh = std::make_unique<UserHeader>(regEntry, severity);
+    _uh = std::make_unique<UserHeader>(regEntry, severity, dataIface);
 
     auto src = std::make_unique<SRC>(regEntry, additionalData, dataIface);
 
diff --git a/extensions/openpower-pels/registry.cpp b/extensions/openpower-pels/registry.cpp
index a02bebb..9b2262d 100644
--- a/extensions/openpower-pels/registry.cpp
+++ b/extensions/openpower-pels/registry.cpp
@@ -68,6 +68,41 @@
     return std::get<pv::fieldValuePos>(*s);
 }
 
+std::vector<RegistrySeverity> getSeverities(const nlohmann::json& severity)
+{
+    std::vector<RegistrySeverity> severities;
+
+    // The plain string value, like "unrecoverable"
+    if (severity.is_string())
+    {
+        RegistrySeverity s;
+        s.severity = getSeverity(severity.get<std::string>());
+        severities.push_back(std::move(s));
+    }
+    else
+    {
+        // An array, with an element like:
+        // {
+        //    "SevValue": "unrecoverable",
+        //    "System", "systemA"
+        // }
+        for (const auto& sev : severity)
+        {
+            RegistrySeverity s;
+            s.severity = getSeverity(sev["SevValue"].get<std::string>());
+
+            if (sev.contains("System"))
+            {
+                s.system = sev["System"].get<std::string>();
+            }
+
+            severities.push_back(std::move(s));
+        }
+    }
+
+    return severities;
+}
+
 uint16_t getActionFlags(const std::vector<std::string>& flags)
 {
     uint16_t actionFlags = 0;
@@ -573,12 +608,12 @@
 
             if (e->contains("Severity"))
             {
-                entry.severity = helper::getSeverity((*e)["Severity"]);
+                entry.severity = helper::getSeverities((*e)["Severity"]);
             }
 
             if (e->contains("MfgSeverity"))
             {
-                entry.mfgSeverity = helper::getSeverity((*e)["MfgSeverity"]);
+                entry.mfgSeverity = helper::getSeverities((*e)["MfgSeverity"]);
             }
 
             if (e->contains("EventType"))
diff --git a/extensions/openpower-pels/registry.hpp b/extensions/openpower-pels/registry.hpp
index 047853d..b145311 100644
--- a/extensions/openpower-pels/registry.hpp
+++ b/extensions/openpower-pels/registry.hpp
@@ -22,6 +22,18 @@
 };
 
 /**
+ * @brief A possible severity/system type combination
+ *
+ * If there is no system type defined for this entry,
+ * then the system field will be empty.
+ */
+struct RegistrySeverity
+{
+    std::string system;
+    uint8_t severity;
+};
+
+/**
  * @brief Represents the Documentation related fields in the message registry.
  *        It is part of the 'Entry' structure that will be filled in when
  *        an error is looked up in the registry.
@@ -122,14 +134,17 @@
     /**
      * @brief The optional PEL severity field.  If not specified, the PEL
      *        will use the severity of the OpenBMC event log.
+     *
+     * If the system type is specified in any of the entries in the vector,
+     * then the system type will be needed to find the actual severity.
      */
-    std::optional<uint8_t> severity;
+    std::optional<std::vector<RegistrySeverity>> severity;
 
     /**
      * @brief The optional severity field to use when in manufacturing tolerance
-     *        mode.
+     *        mode.  It behaves like the severity field above.
      */
-    std::optional<uint8_t> mfgSeverity;
+    std::optional<std::vector<RegistrySeverity>> mfgSeverity;
 
     /**
      * @brief The PEL action flags field.
@@ -319,6 +334,30 @@
 uint8_t getSeverity(const std::string& severityName);
 
 /**
+ * @brief Returns all of the system type/severity values found
+ * in the severity JSON passed in.
+ *
+ * The JSON is either a simple string, like:
+ *     "unrecoverable"
+ * or an array of system type/severity pairs, like:
+ *     [
+ *        {
+ *            "System": "1",
+ *            "SevValue": "predictive"
+ *        },
+ *        {
+ *            "System": "2",
+ *            "SevValue": "recovered"
+ *        }
+ *     ]
+ *
+ * @param[in] severity - The severity JSON
+ * @return The list of severity/system combinations.  If the System key
+ *         wasn't used, then that field will be empty in the structure.
+ */
+std::vector<RegistrySeverity> getSeverities(const nlohmann::json& severity);
+
+/**
  * @brief A helper function to get the action flags value based on
  *        the action flag names used in the registry.
  *
diff --git a/extensions/openpower-pels/user_header.cpp b/extensions/openpower-pels/user_header.cpp
index 8984dc7..07c3de3 100644
--- a/extensions/openpower-pels/user_header.cpp
+++ b/extensions/openpower-pels/user_header.cpp
@@ -46,7 +46,8 @@
 }
 
 UserHeader::UserHeader(const message::Entry& entry,
-                       phosphor::logging::Entry::Level severity)
+                       phosphor::logging::Entry::Level severity,
+                       const DataInterfaceBase& dataIface)
 {
     _header.id = static_cast<uint16_t>(SectionID::userHeader);
     _header.size = UserHeader::flattenedSize();
@@ -61,8 +62,32 @@
 
     // Get the severity from the registry if it's there, otherwise get it
     // from the OpenBMC event log severity value.
-    _eventSeverity =
-        entry.severity.value_or(convertOBMCSeverityToPEL(severity));
+    if (!entry.severity)
+    {
+        _eventSeverity = convertOBMCSeverityToPEL(severity);
+    }
+    else
+    {
+        // Find the severity possibly dependent on the system type.
+        auto sev =
+            getSeverity(entry.severity.value(), dataIface.getSystemType());
+        if (sev)
+        {
+            _eventSeverity = *sev;
+        }
+        else
+        {
+            // Someone screwed up the message registry.
+            log<level::ERR>(
+                "No severity entry found for this error and system type",
+                phosphor::logging::entry("ERROR=%s", entry.name.c_str()),
+                phosphor::logging::entry("SYSTEMTYPE=%s",
+                                         dataIface.getSystemType().c_str()));
+
+            // Have to choose something, just use informational.
+            _eventSeverity = 0;
+        }
+    }
 
     // TODO: ibm-dev/dev/#1144 Handle manufacturing sev & action flags
 
@@ -166,5 +191,35 @@
     uh.erase(uh.size() - 2);
     return uh;
 }
+
+std::optional<uint8_t> UserHeader::getSeverity(
+    const std::vector<message::RegistrySeverity>& severities,
+    const std::string& systemType) const
+{
+    const uint8_t* s = nullptr;
+
+    // Find the severity to use for this system type, or use the default
+    // entry (where no system type is specified).
+    for (const auto& sev : severities)
+    {
+        if (sev.system == systemType)
+        {
+            s = &sev.severity;
+            break;
+        }
+        else if (sev.system.empty())
+        {
+            s = &sev.severity;
+        }
+    }
+
+    if (s)
+    {
+        return *s;
+    }
+
+    return std::nullopt;
+}
+
 } // namespace pels
 } // namespace openpower
diff --git a/extensions/openpower-pels/user_header.hpp b/extensions/openpower-pels/user_header.hpp
index f1fc66d..a033ac0 100644
--- a/extensions/openpower-pels/user_header.hpp
+++ b/extensions/openpower-pels/user_header.hpp
@@ -1,5 +1,6 @@
 #pragma once
 
+#include "data_interface.hpp"
 #include "elog_entry.hpp"
 #include "pel_values.hpp"
 #include "registry.hpp"
@@ -42,9 +43,11 @@
      *
      * @param[in] entry - The message registry entry for this error
      * @param[in] severity - The OpenBMC event log severity for this error
+     * @param[in] dataIface - The DataInterface object
      */
     UserHeader(const message::Entry& entry,
-               phosphor::logging::Entry::Level severity);
+               phosphor::logging::Entry::Level severity,
+               const DataInterfaceBase& dataIface);
 
     /**
      * @brief Constructor
@@ -233,6 +236,20 @@
     void validate() override;
 
     /**
+     * @brief Returns the severity value to use from the list
+     *        of them passed in based on the system type.
+     *
+     * If there isn't an entry found for the current system
+     * type then std::nullopt will be returned.
+     *
+     * @param[in] severities - The array of {systype, severity}
+     *                         structures to find an entry in.
+     * @param[in] systemType - The system type from DataInterface.
+     */
+    std::optional<uint8_t>
+        getSeverity(const std::vector<message::RegistrySeverity>& severities,
+                    const std::string& systemType) const;
+    /**
      * @brief The subsystem associated with the event.
      */
     uint8_t _eventSubsystem;