Log PEL and dump VPD if invalid record(s) found

This commit adds changes in ipz parser to log predictive PEL if any
invalid record(s) is found while parsing. Processing of records still
continues even if any invalid record is found. At the end of parsing the
VPD:
1. A predictive PEL is logged which specifies FRU path and list of
   invalid records.
2. The bad VPD binary data is dumped to filesystem so that it can be
   collected by FFDC.

Test:
```
1. Using dd command, zero out VINI DR and VMPU VZ  keyword values of Op
   Panel EEPROM(/sys/bus/i2c/drivers/at24/7-0051/eeprom).
2. Restart vpd-manager service.
3. Check PEL list using peltool and observe that a predictive PEL is
   logged with the Op Panel EEPROM path in the description and a list of
   bad record names and respective error(Data/ECC) in the UserData1
   section.
4. Check /var/lib/vpd/ directory and see dumps directory is created with
   a dump file which contains the Op Panel EEPROM binary data
5. Restore the Op Panel EEPROM with backed up correct data
6. Clear all PELs using peltool
7. Restart vpd-manager
8. Observe no new PELs logged for invalid records
```

Change-Id: Iebb526bae3c467b1cd0a256c93d57a56fb87e9f6
Signed-off-by: Souvik Roy <souvikroyofficial10@gmail.com>
diff --git a/vpd-manager/src/event_logger.cpp b/vpd-manager/src/event_logger.cpp
index d5bf3d2..a114df5 100644
--- a/vpd-manager/src/event_logger.cpp
+++ b/vpd-manager/src/event_logger.cpp
@@ -423,4 +423,13 @@
 
     return *l_ptrToErrMsg;
 }
+
+std::string EventLogger::getErrorTypeString(
+    const types::ErrorType& i_errorType) noexcept
+{
+    const auto l_entry = m_errorMsgMap.find(i_errorType);
+    return (l_entry != m_errorMsgMap.end()
+                ? l_entry->second
+                : m_errorMsgMap.at(types::ErrorType::UndefinedError));
+}
 } // namespace vpd
diff --git a/vpd-manager/src/ipz_parser.cpp b/vpd-manager/src/ipz_parser.cpp
index a465a03..828c17c 100644
--- a/vpd-manager/src/ipz_parser.cpp
+++ b/vpd-manager/src/ipz_parser.cpp
@@ -7,6 +7,7 @@
 #include "constants.hpp"
 #include "event_logger.hpp"
 #include "exceptions.hpp"
+#include "utility/vpd_specific_utility.hpp"
 
 #include <nlohmann/json.hpp>
 
@@ -242,11 +243,15 @@
     return ptLen;
 }
 
-types::RecordOffsetList IpzVpdParser::readPT(
-    types::BinaryVector::const_iterator& itrToPT, auto ptLength)
+std::pair<types::RecordOffsetList, types::InvalidRecordList>
+    IpzVpdParser::readPT(types::BinaryVector::const_iterator& itrToPT,
+                         auto ptLength)
 {
     types::RecordOffsetList recordOffsets;
 
+    // List of names of all invalid records found.
+    types::InvalidRecordList l_invalidRecordList;
+
     auto end = itrToPT;
     std::advance(end, ptLength);
 
@@ -268,35 +273,13 @@
                 throw(EccException("ERROR: ECC check failed"));
             }
         }
-        catch (const EccException& ex)
+        catch (const std::exception& l_ex)
         {
-            logging::logMessage(ex.what());
+            logging::logMessage(l_ex.what());
 
-            /*TODO: uncomment when PEL code goes in */
-
-            /*std::string errMsg =
-                std::string{ex.what()} + " Record: " + recordName;
-
-            inventory::PelAdditionalData additionalData{};
-            additionalData.emplace("DESCRIPTION", errMsg);
-            additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath);
-            createPEL(additionalData, PelSeverity::WARNING,
-                      errIntfForEccCheckFail, nullptr);*/
-        }
-        catch (const DataException& ex)
-        {
-            logging::logMessage(ex.what());
-
-            /*TODO: uncomment when PEL code goes in */
-
-            /*std::string errMsg =
-                std::string{ex.what()} + " Record: " + recordName;
-
-            inventory::PelAdditionalData additionalData{};
-            additionalData.emplace("DESCRIPTION", errMsg);
-            additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath);
-            createPEL(additionalData, PelSeverity::WARNING,
-                      errIntfForInvalidVPD, nullptr);*/
+            // add the invalid record name and exception object to list
+            l_invalidRecordList.emplace_back(types::InvalidRecordEntry{
+                recordName, EventLogger::getErrorType(l_ex)});
         }
 
         // Jump record size, record length, ECC offset and ECC length
@@ -305,7 +288,7 @@
                          sizeof(types::ECCOffset) + sizeof(types::ECCLength));
     }
 
-    return recordOffsets;
+    return std::make_pair(recordOffsets, l_invalidRecordList);
 }
 
 types::IPZVpdMap::mapped_type IpzVpdParser::readKeywords(
@@ -403,12 +386,19 @@
 
         // Read the table of contents record, to get offsets
         // to other records.
-        auto recordOffsets = readPT(itrToVPD, ptLen);
+        auto l_result = readPT(itrToVPD, ptLen);
+        auto recordOffsets = l_result.first;
         for (const auto& offset : recordOffsets)
         {
             processRecord(offset);
         }
 
+        if (!processInvalidRecords(l_result.second))
+        {
+            logging::logMessage("Failed to process invalid records for [" +
+                                m_vpdFilePath + "]");
+        }
+
         return m_parsedVPDMap;
     }
     catch (const std::exception& e)
@@ -821,4 +811,52 @@
 
     return l_sizeWritten;
 }
+
+bool IpzVpdParser::processInvalidRecords(
+    const types::InvalidRecordList& i_invalidRecordList) const noexcept
+{
+    bool l_rc{true};
+    if (!i_invalidRecordList.empty())
+    {
+        auto l_invalidRecordToString =
+            [](const types::InvalidRecordEntry& l_record) {
+                return std::string{
+                    "{" + l_record.first + "," +
+                    EventLogger::getErrorTypeString(l_record.second) + "}"};
+            };
+
+        std::string l_invalidRecordListString{"["};
+        try
+        {
+            for (const auto& l_entry : i_invalidRecordList)
+            {
+                l_invalidRecordListString +=
+                    l_invalidRecordToString(l_entry) + ",";
+            }
+            l_invalidRecordListString += "]";
+        }
+        catch (const std::exception& l_ex)
+        {
+            l_invalidRecordListString = "";
+        }
+
+        // Log a Predictive PEL, including names and respective error messages
+        // of all invalid records
+        EventLogger::createSyncPel(
+            types::ErrorType::InvalidVpdMessage, types::SeverityType::Warning,
+            __FILE__, __FUNCTION__, constants::VALUE_0,
+            std::string("Invalid records found while parsing VPD for [" +
+                        m_vpdFilePath + "]"),
+            l_invalidRecordListString, std::nullopt, std::nullopt,
+            std::nullopt);
+
+        // Dump Bad VPD to file
+        if (constants::SUCCESS !=
+            vpdSpecificUtility::dumpBadVpd(m_vpdFilePath, m_vpdVector))
+        {
+            l_rc = false;
+        }
+    }
+    return l_rc;
+}
 } // namespace vpd