PEL: Create PEL from parameters

Add a constructor to the PEL class so it can be built from the message
registry entry for that error along with the event log properties.

When support for new sections are added, they will be created here as
well along with the PrivateHeader and UserHeader section classes.

Eventually, this constructor will be called from the Manager class after
it is told a new event log has been created and after it looks up that
error's PEL message registry entry.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I2d8ca550736aab45a30ac3db9861723b33e6cd32
diff --git a/extensions/openpower-pels/pel.cpp b/extensions/openpower-pels/pel.cpp
index 1eac869..6a56e15 100644
--- a/extensions/openpower-pels/pel.cpp
+++ b/extensions/openpower-pels/pel.cpp
@@ -8,6 +8,19 @@
 {
 namespace pels
 {
+namespace message = openpower::pels::message;
+
+PEL::PEL(const message::Entry& entry, uint32_t obmcLogID, uint64_t timestamp,
+         phosphor::logging::Entry::Level severity)
+{
+    _ph = std::make_unique<PrivateHeader>(entry.componentID, obmcLogID,
+                                          timestamp);
+    _uh = std::make_unique<UserHeader>(entry, severity);
+
+    // Add future sections here and update the count
+
+    _ph->sectionCount() = 2;
+}
 
 PEL::PEL(const std::vector<uint8_t>& data) : PEL(data, 0)
 {
@@ -15,6 +28,7 @@
 
 PEL::PEL(const std::vector<uint8_t>& data, uint32_t obmcLogID) : _rawPEL(data)
 {
+    _fromStream = true;
     populateFromRawData(obmcLogID);
 }
 
@@ -56,26 +70,26 @@
 void PEL::flatten(std::vector<uint8_t>& pelBuffer)
 {
     Stream pelData{pelBuffer};
-    if (_ph->valid())
-    {
-        _ph->flatten(pelData);
-    }
-    else
+
+    _ph->flatten(pelData);
+
+    // If constructed from a PEL stream originally, don't flatten the
+    // rest of the objects until we support every PEL section type.
+    // Still need the PrivateHeader, as we updated fields in it.
+    if (_fromStream)
     {
         return;
     }
 
-    if (_uh->valid())
-    {
-        _uh->flatten(pelData);
-    }
+    _uh->flatten(pelData);
 }
 
 std::vector<uint8_t> PEL::data()
 {
     // Until we can recreate a complete PEL from objects, need to just flatten
     // on top of the original PEL data which we need to keep around for this
-    // reason.
+    // reason.  If creating a PEL from scratch, _rawPEL will get filled in with
+    // what we do have.
 
     flatten(_rawPEL);
     return _rawPEL;
diff --git a/extensions/openpower-pels/pel.hpp b/extensions/openpower-pels/pel.hpp
index 564fe33..ff45acb 100644
--- a/extensions/openpower-pels/pel.hpp
+++ b/extensions/openpower-pels/pel.hpp
@@ -1,6 +1,8 @@
 #pragma once
 
+#include "additional_data.hpp"
 #include "private_header.hpp"
+#include "registry.hpp"
 #include "user_header.hpp"
 
 #include <memory>
@@ -33,6 +35,12 @@
  * - PEL(const std::vector<uint8_t>& data) - build this object out of a fully
  *   formed flattened PEL.
  *
+ * - PEL(const openpower::pels::message::Entry& entry,
+ *       uint32_t obmcLogID,
+ *       uint64_t timestamp,
+ *       phosphor::logging::Entry::Level severity)
+ *      - build this object from an OpenBMC event log.
+ *
  * The data() method allows one to retrieve the PEL as a vector<uint8_t>.  This
  * is the format in which it is stored and transmitted.
  */
@@ -66,6 +74,20 @@
     PEL(const std::vector<uint8_t>& data, uint32_t obmcLogID);
 
     /**
+     * @brief Constructor
+     *
+     * Creates a PEL from an OpenBMC event log and its message
+     * registry entry.
+     *
+     * @param[in] entry - The message registry entry for this error
+     * @param[in] obmcLogID - ID of corresponding OpenBMC event log
+     * @param[in] timestamp - Timestamp from the event log
+     * @param[in] severity - Severity from the event log
+     */
+    PEL(const openpower::pels::message::Entry& entry, uint32_t obmcLogID,
+        uint64_t timestamp, phosphor::logging::Entry::Level severity);
+
+    /**
      * @brief Convenience function to return the log ID field from the
      *        Private Header section.
      *
@@ -198,6 +220,11 @@
      * then there will be no need to keep around the data anymore.
      */
     std::vector<uint8_t> _rawPEL;
+
+    /**
+     * @brief If the PEL came from a flattened data stream.
+     */
+    bool _fromStream = false;
 };
 
 } // namespace pels
diff --git a/test/openpower-pels/pel_test.cpp b/test/openpower-pels/pel_test.cpp
index ba71dc2..6d888b7 100644
--- a/test/openpower-pels/pel_test.cpp
+++ b/test/openpower-pels/pel_test.cpp
@@ -1,3 +1,4 @@
+#include "elog_entry.hpp"
 #include "extensions/openpower-pels/pel.hpp"
 #include "pel_utils.hpp"
 
@@ -86,10 +87,6 @@
     EXPECT_FALSE(pel->userHeader()->valid());
     EXPECT_FALSE(pel->valid());
 
-    // Ensure we can still flatten bad data
-    auto newData = pel->data();
-    EXPECT_EQ(*data, newData);
-
     // Now corrupt the private header
     data = pelDataFactory(TestPelType::pelSimple);
     data->at(0) = 0;
@@ -109,3 +106,21 @@
     EXPECT_FALSE(pel->userHeader()->valid());
     EXPECT_FALSE(pel->valid());
 }
+
+TEST_F(PELTest, CreateFromRegistryTest)
+{
+    message::Entry regEntry;
+    uint64_t timestamp = 5;
+
+    regEntry.name = "test";
+    regEntry.subsystem = 5;
+    regEntry.actionFlags = 0xC000;
+
+    PEL pel{regEntry, 42, timestamp, phosphor::logging::Entry::Level::Error};
+
+    EXPECT_TRUE(pel.valid());
+    EXPECT_EQ(pel.privateHeader()->obmcLogID(), 42);
+    EXPECT_EQ(pel.userHeader()->severity(), 0x40);
+
+    // Add more checks as more sections are added
+}