PEL: Still create PEL when error not in registry

When an OpenBMC event log is created for an error that isn't in the PEL
message registry, the code will now find a registry entry using another
default error name.  This default PEL will contain the severity and any
callouts specified by the original event log, and its message used by
peltool and possibly Redfish will state that the actual meaning for the
error is unknown.

The error name property that was missing from the registry will be
placed in the JSON UserData section that hold the AdditionalData
property fields under the key ERROR_NAME.

Doing this allows one to see all errors on a system just by looking at
PELs, and not miss important ones just because a developer doesn't have
their error in the registry yet.

When these are seen, it may be a good idea to create bug reports as a
reminder that the missing error needs to be added.

Change-Id: Ifa3db21af96745dd651da5f5af0ab970a952f46a
diff --git a/extensions/openpower-pels/manager.cpp b/extensions/openpower-pels/manager.cpp
index d7fbcad..c8b5872 100644
--- a/extensions/openpower-pels/manager.cpp
+++ b/extensions/openpower-pels/manager.cpp
@@ -44,8 +44,11 @@
 {
 constexpr auto rawPEL = "RAWPEL";
 constexpr auto esel = "ESEL";
+constexpr auto error = "ERROR_NAME";
 } // namespace additional_data
 
+constexpr auto defaultLogMessage = "xyz.openbmc_project.Logging.Error.Default";
+
 Manager::~Manager()
 {
     if (_pelFileDeleteFD != -1)
@@ -286,43 +289,53 @@
                         const FFDCEntries& ffdc)
 {
     auto entry = _registry.lookup(message, rg::LookupType::name);
+    auto pelFFDC = convertToPelFFDC(ffdc);
+    AdditionalData ad{additionalData};
     std::string msg;
 
-    if (entry)
+    if (!entry)
     {
-        AdditionalData ad{additionalData};
-
-        auto pelFFDC = convertToPelFFDC(ffdc);
-
-        auto pel = std::make_unique<openpower::pels::PEL>(
-            *entry, obmcLogID, timestamp, severity, ad, pelFFDC, *_dataIface);
-
-        _repo.add(pel);
-
-        if (_repo.sizeWarning())
-        {
-            scheduleRepoPrune();
-        }
-
-        auto src = pel->primarySRC();
-        if (src)
-        {
-            using namespace std::literals::string_literals;
-            auto id = getNumberString("0x%08X", pel->id());
-            msg = "Created PEL "s + id + " with SRC "s + (*src)->asciiString();
-            while (msg.back() == ' ')
-            {
-                msg.pop_back();
-            }
-            log<level::INFO>(msg.c_str());
-        }
-    }
-    else
-    {
-        // TODO ibm-openbmc/dev/1151: Create a new PEL for this case.
-        // For now, just trace it.
+        // Instead, get the default entry that means there is no
+        // other matching entry.  This error will still use the
+        // AdditionalData values of the original error, and this
+        // code will add the error message value that wasn't found
+        // to this AD.  This way, there will at least be a PEL,
+        // possibly with callouts, to allow users to debug the
+        // issue that caused the error even without its own PEL.
         msg = "Event not found in PEL message registry: " + message;
         log<level::INFO>(msg.c_str());
+
+        entry = _registry.lookup(defaultLogMessage, rg::LookupType::name);
+        if (!entry)
+        {
+            log<level::ERR>("Default event not found in PEL message registry");
+            return;
+        }
+
+        ad.add(additional_data::error, message);
+    }
+
+    auto pel = std::make_unique<openpower::pels::PEL>(
+        *entry, obmcLogID, timestamp, severity, ad, pelFFDC, *_dataIface);
+
+    _repo.add(pel);
+
+    if (_repo.sizeWarning())
+    {
+        scheduleRepoPrune();
+    }
+
+    auto src = pel->primarySRC();
+    if (src)
+    {
+        using namespace std::literals::string_literals;
+        auto id = getNumberString("0x%08X", pel->id());
+        msg = "Created PEL "s + id + " with SRC "s + (*src)->asciiString();
+        while (msg.back() == ' ')
+        {
+            msg.pop_back();
+        }
+        log<level::INFO>(msg.c_str());
     }
 }
 
diff --git a/extensions/openpower-pels/registry/message_registry.json b/extensions/openpower-pels/registry/message_registry.json
index b4bf308..fcb0850 100644
--- a/extensions/openpower-pels/registry/message_registry.json
+++ b/extensions/openpower-pels/registry/message_registry.json
@@ -344,6 +344,36 @@
         },
 
         {
+            "Name": "xyz.openbmc_project.Logging.Error.Default",
+            "Subsystem": "other_na",
+
+            "SRC":
+            {
+                "ReasonCode": "0x2004",
+                "Words6To9":
+                {
+                }
+            },
+
+            "Documentation":
+            {
+                "Description": "This entry is used when no other matching entry is found",
+                "Message": "Unknown (Original event not in message registry)",
+                "Notes": [
+                    "This is used when the actual error name isn't found in ",
+                    "the registry.  The original error name will be stored ",
+                    "in the UserData section that contains the AdditionalData ",
+                    "properties using the key ERROR_NAME.",
+                    "This error may contain callouts if the creator passed ",
+                    "them in.",
+                    "The severity is set by the creator.",
+                    "If this error is seen, then a code change needs to be ",
+                    "made to add the missing error entry to this registry."
+                ]
+            }
+        },
+
+        {
             "Name": "org.open_power.PHAL.Error.Boot",
             "Subsystem": "cec_hardware",
             "Severity": "unrecoverable",
diff --git a/test/openpower-pels/pel_manager_test.cpp b/test/openpower-pels/pel_manager_test.cpp
index b7146c9..bf1a18f 100644
--- a/test/openpower-pels/pel_manager_test.cpp
+++ b/test/openpower-pels/pel_manager_test.cpp
@@ -235,6 +235,19 @@
                 "Description": "A PGOOD Fault",
                 "Message": "PS had a PGOOD Fault"
             }
+        },
+        {
+            "Name": "xyz.openbmc_project.Logging.Error.Default",
+            "Subsystem": "bmc_firmware",
+            "SRC":
+            {
+                "ReasonCode": "0x2031"
+            },
+            "Documentation":
+            {
+                "Description": "The entry used when no match found",
+                "Message": "This is a generic SRC"
+            }
         }
     ]
 }
@@ -256,7 +269,7 @@
         std::bind(std::mem_fn(&TestLogger::log), &logger, std::placeholders::_1,
                   std::placeholders::_2, std::placeholders::_3)};
 
-    std::vector<std::string> additionalData;
+    std::vector<std::string> additionalData{"FOO=BAR"};
     std::vector<std::string> associations;
 
     // Create the event log to create the PEL from.
@@ -283,14 +296,64 @@
     EXPECT_FALSE(pelFile);
 
     // Create an event log that can't be found in the registry.
-    manager.create("xyz.openbmc_project.Error.Foo", 33, 0,
+    // In this case, xyz.openbmc_project.Logging.Error.Default will
+    // be used as the key instead to find a registry match.
+    manager.create("xyz.openbmc_project.Error.Foo", 42, 0,
                    phosphor::logging::Entry::Level::Error, additionalData,
                    associations);
 
-    // Currently, no PEL should be created.  Eventually, a 'missing registry
-    // entry' PEL will be there.
+    // Ensure a PEL was still created in the repository
     pelFile = findAnyPELInRepo();
-    EXPECT_FALSE(pelFile);
+    ASSERT_TRUE(pelFile);
+
+    data = readPELFile(*pelFile);
+    PEL newPEL(*data);
+
+    EXPECT_TRUE(newPEL.valid());
+    EXPECT_EQ(newPEL.obmcLogID(), 42);
+    EXPECT_EQ(newPEL.primarySRC().value()->asciiString(),
+              "BD8D2031                        ");
+
+    // Check for both the original AdditionalData item as well as
+    // the ERROR_NAME item that should contain the error message
+    // property that wasn't found.
+    std::string errorName;
+    std::string adItem;
+
+    for (const auto& section : newPEL.optionalSections())
+    {
+        if (SectionID::userData == static_cast<SectionID>(section->header().id))
+        {
+            if (UserDataFormat::json ==
+                static_cast<UserDataFormat>(section->header().subType))
+            {
+                auto ud = static_cast<UserData*>(section.get());
+
+                // Check that there was a UserData section added that
+                // contains debug details about the device.
+                const auto& d = ud->data();
+                std::string jsonString{d.begin(), d.end()};
+                auto json = nlohmann::json::parse(jsonString);
+
+                if (json.contains("ERROR_NAME"))
+                {
+                    errorName = json["ERROR_NAME"].get<std::string>();
+                }
+
+                if (json.contains("FOO"))
+                {
+                    adItem = json["FOO"].get<std::string>();
+                }
+            }
+        }
+        if (!errorName.empty())
+        {
+            break;
+        }
+    }
+
+    EXPECT_EQ(errorName, "xyz.openbmc_project.Error.Foo");
+    EXPECT_EQ(adItem, "BAR");
 }
 
 TEST_F(ManagerTest, TestDBusMethods)