PEL: Save BMC Version ID in UserData section

When creating a PEL, save the VERSION_ID value from the /etc/os-release
file in the UserData section that keeps useful system information.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I6d9008b15c5347239bf8c21ef79219d3b6ee08e6
diff --git a/extensions/openpower-pels/data_interface.cpp b/extensions/openpower-pels/data_interface.cpp
index 6ad8b6e..3342569 100644
--- a/extensions/openpower-pels/data_interface.cpp
+++ b/extensions/openpower-pels/data_interface.cpp
@@ -55,6 +55,7 @@
     readHostState();
     readBMCFWVersion();
     readServerFWVersion();
+    readBMCFWVersionID();
 }
 
 void DataInterface::readMTMS()
@@ -244,21 +245,35 @@
 #endif
 }
 
-void DataInterface::readBMCFWVersion()
+/**
+ * @brief Return a value found in the /etc/os-release file
+ *
+ * @param[in] key - The key name, like "VERSION"
+ *
+ * @return std::optional<std::string> - The value
+ */
+std::optional<std::string> getOSReleaseValue(const std::string& key)
 {
     std::ifstream versionFile{BMC_VERSION_FILE};
     std::string line;
-    static const auto versionID = "VERSION=";
+    std::string keyPattern{key + '='};
 
     while (std::getline(versionFile, line))
     {
-        if (line.find(versionID) != std::string::npos)
+        if (line.find(keyPattern) != std::string::npos)
         {
             auto pos = line.find_first_of('"') + 1;
-            _bmcFWVersion = line.substr(pos, line.find_last_of('"') - pos);
-            break;
+            auto value = line.substr(pos, line.find_last_of('"') - pos);
+            return value;
         }
     }
+
+    return std::nullopt;
+}
+
+void DataInterface::readBMCFWVersion()
+{
+    _bmcFWVersion = getOSReleaseValue("VERSION").value_or("");
 }
 
 void DataInterface::readServerFWVersion()
@@ -266,5 +281,10 @@
     // Not available yet
 }
 
+void DataInterface::readBMCFWVersionID()
+{
+    _bmcFWVersionID = getOSReleaseValue("VERSION_ID").value_or("");
+}
+
 } // namespace pels
 } // namespace openpower
diff --git a/extensions/openpower-pels/data_interface.hpp b/extensions/openpower-pels/data_interface.hpp
index e690542..e139c07 100644
--- a/extensions/openpower-pels/data_interface.hpp
+++ b/extensions/openpower-pels/data_interface.hpp
@@ -137,6 +137,16 @@
     }
 
     /**
+     * @brief Returns the BMC FW version ID
+     *
+     * @return std::string - The BMC FW version ID
+     */
+    virtual std::string getBMCFWVersionID() const
+    {
+        return _bmcFWVersionID;
+    }
+
+    /**
      * @brief Returns the process name given its PID.
      *
      * @param[in] pid - The PID value as a string
@@ -221,6 +231,11 @@
      * @brief The server firmware version string
      */
     std::string _serverFWVersion;
+
+    /**
+     * @brief The BMC firmware version ID string
+     */
+    std::string _bmcFWVersionID;
 };
 
 /**
@@ -291,6 +306,12 @@
     void readServerFWVersion();
 
     /**
+     * @brief Reads the BMC firmware version ID and puts it into
+     *        _bmcFWVersionID.
+     */
+    void readBMCFWVersionID();
+
+    /**
      * @brief Finds the D-Bus service name that hosts the
      *        passed in path and interface.
      *
diff --git a/extensions/openpower-pels/pel.cpp b/extensions/openpower-pels/pel.cpp
index 0c05ac1..e560d5e 100644
--- a/extensions/openpower-pels/pel.cpp
+++ b/extensions/openpower-pels/pel.cpp
@@ -37,6 +37,8 @@
 namespace message = openpower::pels::message;
 namespace pv = openpower::pels::pel_values;
 
+constexpr auto unknownValue = "Unknown";
+
 PEL::PEL(const message::Entry& entry, uint32_t obmcLogID, uint64_t timestamp,
          phosphor::logging::Entry::Level severity,
          const AdditionalData& additionalData,
@@ -343,7 +345,7 @@
                           const std::optional<std::string>& pid,
                           const DataInterfaceBase& dataIface)
 {
-    std::string name = "Unknown";
+    std::string name{unknownValue};
 
     try
     {
@@ -363,6 +365,18 @@
     json["Process Name"] = std::move(name);
 }
 
+void addBMCFWVersionIDToJSON(nlohmann::json& json,
+                             const DataInterfaceBase& dataIface)
+{
+    auto id = dataIface.getBMCFWVersionID();
+    if (id.empty())
+    {
+        id = unknownValue;
+    }
+
+    json["BMC Version ID"] = std::move(id);
+}
+
 std::unique_ptr<UserData>
     makeSysInfoUserDataSection(const AdditionalData& ad,
                                const DataInterfaceBase& dataIface)
@@ -370,6 +384,7 @@
     nlohmann::json json;
 
     addProcessNameToJSON(json, ad.getValue("_PID"), dataIface);
+    addBMCFWVersionIDToJSON(json, dataIface);
 
     return makeJSONUserDataSection(json);
 }
diff --git a/test/openpower-pels/mocks.hpp b/test/openpower-pels/mocks.hpp
index 5bec579..e00e006 100644
--- a/test/openpower-pels/mocks.hpp
+++ b/test/openpower-pels/mocks.hpp
@@ -23,6 +23,7 @@
     MOCK_METHOD(std::string, getMachineSerialNumber, (), (const override));
     MOCK_METHOD(std::string, getServerFWVersion, (), (const override));
     MOCK_METHOD(std::string, getBMCFWVersion, (), (const override));
+    MOCK_METHOD(std::string, getBMCFWVersionID, (), (const override));
 
     void changeHostState(bool newState)
     {
diff --git a/test/openpower-pels/pel_test.cpp b/test/openpower-pels/pel_test.cpp
index 1a5f81f..32f8d5e 100644
--- a/test/openpower-pels/pel_test.cpp
+++ b/test/openpower-pels/pel_test.cpp
@@ -26,6 +26,7 @@
 
 namespace fs = std::filesystem;
 using namespace openpower::pels;
+using ::testing::Return;
 
 class PELTest : public CleanLogID
 {
@@ -313,10 +314,12 @@
 }
 
 // Create the UserData section that contains system info
-TEST_F(PELTest, MakeSysInfoSectionTest)
+TEST_F(PELTest, SysInfoSectionTest)
 {
     MockDataInterface dataIface;
 
+    EXPECT_CALL(dataIface, getBMCFWVersionID()).WillOnce(Return("ABCD1234"));
+
     std::string pid = "_PID=" + std::to_string(getpid());
     std::vector<std::string> ad{pid};
     AdditionalData additionalData{ad};
@@ -337,4 +340,7 @@
     // Ensure the 'Process Name' entry contains 'pel_test'
     auto name = json["Process Name"].get<std::string>();
     EXPECT_NE(name.find("pel_test"), std::string::npos);
+
+    auto version = json["BMC Version ID"].get<std::string>();
+    EXPECT_EQ(version, "ABCD1234");
 }