PEL: Print action flags into JSON aligned

"User Header": {
    "Section Version":          "1",
    "Sub-section type":         "0",
    "Log Committed by":         "0x4552",
    "Subsystem":                "System Hypervisor Firmware",
    "Event Scope":              "Entire Platform",
    "Event Severity":           "Informational Event",
    "Event Type":               "Miscellaneous, Informational Only",
    "Action Flags": [
                                "Report Externally"
    ]
}

Testing: Manually run peltool and verified output
Signed-off-by: Harisuddin Mohamed Isa <harisuddin@gmail.com>
Change-Id: Ie8376953b5f1baa093fc0aa9564d50cd4208564e
diff --git a/extensions/openpower-pels/hexdump.hpp b/extensions/openpower-pels/hexdump.hpp
deleted file mode 100644
index bd26884..0000000
--- a/extensions/openpower-pels/hexdump.hpp
+++ /dev/null
@@ -1,33 +0,0 @@
-#pragma once
-
-#include <ctype.h>
-#include <stdio.h>
-
-#include <fstream>
-#include <iomanip>
-#include <iostream>
-#include <string>
-#include <vector>
-
-namespace openpower
-{
-namespace pels
-{
-
-/**
- * @brief escape json - use it for PEL hex dumps.
- * @param[in] std::string - the unescaped JSON as a string literal
- * @return std::string - escaped JSON string literal
- */
-std::string escapeJSON(const std::string& input);
-
-/**
- * @brief get hex dump for PEL section in json format.
- * @param[in] const void* - Raw PEL data
- * @param[i] size_t - size of Raw PEL
- * @return char * - the Hex dump
- */
-char* dumpHex(const void* data, size_t size);
-
-} // namespace pels
-} // namespace openpower
diff --git a/extensions/openpower-pels/hexdump.cpp b/extensions/openpower-pels/json_utils.cpp
similarity index 63%
rename from extensions/openpower-pels/hexdump.cpp
rename to extensions/openpower-pels/json_utils.cpp
index ee4aef8..c6ea9f6 100644
--- a/extensions/openpower-pels/hexdump.cpp
+++ b/extensions/openpower-pels/json_utils.cpp
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include "hexdump.hpp"
+#include "json_utils.hpp"
 
 #include <stdio.h>
 
@@ -70,6 +70,8 @@
 char* dumpHex(const void* data, size_t size)
 {
     const int symbolSize = 100;
+    std::string jsonIndent(indentLevel, 0x20);
+    jsonIndent.append("\"");
     char* buffer = (char*)calloc(10 * size, sizeof(char));
     char* symbol = (char*)calloc(symbolSize, sizeof(char));
     char ascii[17];
@@ -79,7 +81,7 @@
     {
         if (i % 16 == 0)
         {
-            strcat(buffer, "\"");
+            strcat(buffer, jsonIndent.c_str());
         }
         snprintf(symbol, symbolSize, "%02X ", ((unsigned char*)data)[i]);
         strcat(buffer, symbol);
@@ -103,11 +105,11 @@
             {
                 if (i + 1 != size)
                 {
-                    snprintf(symbol, symbolSize, "|  %s\", \n ", asciiToPrint);
+                    snprintf(symbol, symbolSize, "|  %s\",\n", asciiToPrint);
                 }
                 else
                 {
-                    snprintf(symbol, symbolSize, "|  %s\" \n ", asciiToPrint);
+                    snprintf(symbol, symbolSize, "|  %s\"\n", asciiToPrint);
                 }
                 strcat(buffer, symbol);
                 memset(symbol, 0, strlen(symbol));
@@ -126,7 +128,7 @@
                 std::string asciiString2(ascii);
                 asciiString2 = escapeJSON(asciiString2);
                 asciiToPrint = asciiString2.c_str();
-                snprintf(symbol, symbolSize, "|  %s\" \n ", asciiToPrint);
+                snprintf(symbol, symbolSize, "|  %s\"\n", asciiToPrint);
                 strcat(buffer, symbol);
                 memset(symbol, 0, strlen(symbol));
             }
@@ -135,5 +137,61 @@
     free(symbol);
     return buffer;
 }
+
+void jsonInsert(std::string& jsonStr, const std::string& fieldName,
+                std::string& fieldValue, uint8_t indentCount)
+{
+    const int8_t spacesToAppend =
+        colAlign - (indentCount * indentLevel) - fieldName.length() - 3;
+    const std::string jsonIndent(indentCount * indentLevel, 0x20);
+    jsonStr.append(jsonIndent + "\"" + fieldName + "\":");
+    if (spacesToAppend > 0)
+    {
+        jsonStr.append(spacesToAppend, 0x20);
+    }
+    else
+    {
+        jsonStr.append(1, 0x20);
+    }
+    jsonStr.append("\"" + fieldValue + "\",\n");
+}
+
+void jsonInsertArray(std::string& jsonStr, const std::string& fieldName,
+                     std::vector<std::string>& values, uint8_t indentCount)
+{
+    const std::string jsonIndent(indentCount * indentLevel, 0x20);
+    if (!values.empty())
+    {
+        jsonStr.append(jsonIndent + "\"" + fieldName + "\": [\n");
+        for (size_t i = 0; i < values.size(); i++)
+        {
+            jsonStr.append(colAlign, 0x20);
+            if (i == values.size() - 1)
+            {
+                jsonStr.append("\"" + values[i] + "\"\n");
+            }
+            else
+            {
+                jsonStr.append("\"" + values[i] + "\",\n");
+            }
+        }
+        jsonStr.append(jsonIndent + "],\n");
+    }
+    else
+    {
+        const int8_t spacesToAppend =
+            colAlign - (indentCount * indentLevel) - fieldName.length() - 3;
+        jsonStr.append(jsonIndent + "\"" + fieldName + "\":");
+        if (spacesToAppend > 0)
+        {
+            jsonStr.append(spacesToAppend, 0x20);
+        }
+        else
+        {
+            jsonStr.append(1, 0x20);
+        }
+        jsonStr.append("[],\n");
+    }
+}
 } // namespace pels
 } // namespace openpower
diff --git a/extensions/openpower-pels/json_utils.hpp b/extensions/openpower-pels/json_utils.hpp
new file mode 100644
index 0000000..14c2ce4
--- /dev/null
+++ b/extensions/openpower-pels/json_utils.hpp
@@ -0,0 +1,55 @@
+#pragma once
+
+#include <ctype.h>
+#include <stdio.h>
+
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <string>
+#include <vector>
+
+namespace openpower
+{
+namespace pels
+{
+const uint8_t indentLevel = 4;
+const uint8_t colAlign = 32;
+/**
+ * @brief escape json - use it for PEL hex dumps.
+ * @param[in] std::string - the unescaped JSON as a string literal
+ * @return std::string - escaped JSON string literal
+ */
+std::string escapeJSON(const std::string& input);
+
+/**
+ * @brief get hex dump for PEL section in json format.
+ * @param[in] const void* - Raw PEL data
+ * @param[i] size_t - size of Raw PEL
+ * @return char * - the Hex dump
+ */
+char* dumpHex(const void* data, size_t size);
+
+/**
+ * @brief Inserts key-value into a JSON string
+ *
+ * @param[in] jsonStr - The JSON string
+ * @param[in] fieldName - The JSON key to insert
+ * @param[in] fieldValue - The JSON value to insert
+ * @param[in] indentCount - Indent count for the line
+ */
+void jsonInsert(std::string& jsonStr, const std::string& fieldName,
+                std::string& fieldValue, uint8_t indentCount);
+
+/**
+ * @brief Inserts key-value array into a JSON string
+ *
+ * @param[in] jsonStr - The JSON string
+ * @param[in] fieldName - The JSON key to insert
+ * @param[in] values - The JSON array to insert
+ * @param[in] indentCount - Indent count for the line
+ */
+void jsonInsertArray(std::string& jsonStr, const std::string& fieldName,
+                     std::vector<std::string>& values, uint8_t indentCount);
+} // namespace pels
+} // namespace openpower
diff --git a/extensions/openpower-pels/openpower-pels.mk b/extensions/openpower-pels/openpower-pels.mk
index 5bce70d..d974e06 100644
--- a/extensions/openpower-pels/openpower-pels.mk
+++ b/extensions/openpower-pels/openpower-pels.mk
@@ -18,7 +18,7 @@
 	extensions/openpower-pels/failing_mtms.cpp \
 	extensions/openpower-pels/fru_identity.cpp \
 	extensions/openpower-pels/generic.cpp \
-	extensions/openpower-pels/hexdump.cpp \
+	extensions/openpower-pels/json_utils.cpp \
 	extensions/openpower-pels/log_id.cpp \
 	extensions/openpower-pels/mru.cpp \
 	extensions/openpower-pels/mtms.cpp \
diff --git a/extensions/openpower-pels/pel.cpp b/extensions/openpower-pels/pel.cpp
index 022ac05..74d46bd 100644
--- a/extensions/openpower-pels/pel.cpp
+++ b/extensions/openpower-pels/pel.cpp
@@ -18,7 +18,7 @@
 #include "bcd_time.hpp"
 #include "extended_user_header.hpp"
 #include "failing_mtms.hpp"
-#include "hexdump.hpp"
+#include "json_utils.hpp"
 #include "log_id.hpp"
 #include "pel_rules.hpp"
 #include "pel_values.hpp"
@@ -229,12 +229,12 @@
         auto json = section.getJSON();
         if (json)
         {
-            buf += "\n\"" + sectionName + "\":[\n ";
-            buf += *json + "\n],\n";
+            buf += "\n\"" + sectionName + "\": {\n";
+            buf += *json + "\n},\n";
         }
         else
         {
-            buf += "\n\"" + sectionName + "\":[\n ";
+            buf += "\n\"" + sectionName + "\": [\n";
             std::vector<uint8_t> data;
             Stream s{data};
             section.flatten(s);
@@ -244,7 +244,7 @@
     }
     else
     {
-        buf += "\n\"Invalid Section  \":[\n invalid \n],\n";
+        buf += "\n\"Invalid Section\": [\n    \"invalid\"\n],\n";
     }
 }
 
@@ -263,5 +263,6 @@
         buf.replace(found, 1, "");
     std::cout << buf << std::endl;
 }
+
 } // namespace pels
 } // namespace openpower
diff --git a/extensions/openpower-pels/pel_values.cpp b/extensions/openpower-pels/pel_values.cpp
index 3502f92..f8c877c 100644
--- a/extensions/openpower-pels/pel_values.cpp
+++ b/extensions/openpower-pels/pel_values.cpp
@@ -196,8 +196,10 @@
     {0x8000, "service_action", "Service Action Required"},
     {0x4000, "hidden", "Event not customer viewable"},
     {0x2000, "report", "Report Externally"},
-    {0x1000, "dont_report", "Do Not Report"},
+    {0x1000, "dont_report", "Do Not Report To Hypervisor"},
     {0x0800, "call_home", "HMC Call Home"},
+    {0x0400, "isolation_incomplete",
+     "Isolation Incomplete, further analysis required"},
     {0x0100, "termination", "Service Processor Call Home Required"}};
 
 /**
@@ -276,6 +278,20 @@
         return "invalid";
     }
 }
+
+std::vector<std::string> getValuesBitwise(uint16_t value,
+                                          const pel_values::PELValues& table)
+{
+    std::vector<std::string> foundValues;
+    std::for_each(
+        table.begin(), table.end(), [&value, &foundValues](const auto& entry) {
+            if (value & std::get<fieldValuePos>(entry))
+            {
+                foundValues.push_back(std::get<descriptionPos>(entry));
+            }
+        });
+    return foundValues;
+}
 } // namespace pel_values
 } // namespace pels
 } // namespace openpower
diff --git a/extensions/openpower-pels/pel_values.hpp b/extensions/openpower-pels/pel_values.hpp
index 8d86acf..9a1ed88 100644
--- a/extensions/openpower-pels/pel_values.hpp
+++ b/extensions/openpower-pels/pel_values.hpp
@@ -33,6 +33,16 @@
 std::string getValue(const uint8_t field, const pel_values::PELValues& values);
 
 /**
+ * @brief Helper function to get value vector from lookup tables.
+ *
+ * @param[in] value - the value to lookup
+ * @param[in] table - lookup table
+ *
+ * @return std::vector<std::string> - the value vector
+ */
+std::vector<std::string> getValuesBitwise(uint16_t value,
+                                          const pel_values::PELValues& table);
+/**
  * @brief Find the desired entry in a PELValues table based on the
  *        field value.
  *
diff --git a/extensions/openpower-pels/user_header.cpp b/extensions/openpower-pels/user_header.cpp
index 5aef28d..b70da94 100644
--- a/extensions/openpower-pels/user_header.cpp
+++ b/extensions/openpower-pels/user_header.cpp
@@ -15,6 +15,7 @@
  */
 #include "user_header.hpp"
 
+#include "json_utils.hpp"
 #include "pel_types.hpp"
 #include "pel_values.hpp"
 #include "severity.hpp"
@@ -135,10 +136,13 @@
     std::string subsystem;
     std::string eventScope;
     std::string eventType;
+    std::vector<std::string> actionFlags;
     severity = pv::getValue(_eventSeverity, pel_values::severityValues);
     subsystem = pv::getValue(_eventSubsystem, pel_values::subsystemValues);
     eventScope = pv::getValue(_eventScope, pel_values::eventScopeValues);
     eventType = pv::getValue(_eventType, pel_values::eventTypeValues);
+    actionFlags =
+        pv::getValuesBitwise(_actionFlags, pel_values::actionFlagsValues);
     char tmpUhVal[8];
     sprintf(tmpUhVal, "%d", userHeaderVersion);
     std::string uhVerStr(tmpUhVal);
@@ -147,15 +151,16 @@
     sprintf(tmpUhVal, "%d", _header.subType);
     std::string uhStStr(tmpUhVal);
 
-    std::string uh = "{\"Section Version\": \"" + uhVerStr +
-                     "\"}, \n {\"Sub-section type\": \"" + uhStStr +
-                     "\"}, \n "
-                     "{\"Log Committed by\": \"" +
-                     uhCbStr + "\"}, \n {\"Subsystem\": \"" + subsystem +
-                     "\"},\n "
-                     "{\"Event Scope\": \"" +
-                     eventScope + "\"}, \n {\"Event Severity\":\"" + severity +
-                     "\"},\n {\"Event Type\": \"" + eventType + "\"}";
+    std::string uh;
+    jsonInsert(uh, "Section Version", uhVerStr, 1);
+    jsonInsert(uh, "Sub-section type", uhStStr, 1);
+    jsonInsert(uh, "Log Committed by", uhCbStr, 1);
+    jsonInsert(uh, "Subsystem", subsystem, 1);
+    jsonInsert(uh, "Event Scope", eventScope, 1);
+    jsonInsert(uh, "Event Severity", severity, 1);
+    jsonInsert(uh, "Event Type", eventType, 1);
+    jsonInsertArray(uh, "Action Flags", actionFlags, 1);
+    uh.erase(uh.size() - 2);
     return uh;
 }
 } // namespace pels
diff --git a/test/openpower-pels/Makefile.include b/test/openpower-pels/Makefile.include
index dfa4156..d85f215 100644
--- a/test/openpower-pels/Makefile.include
+++ b/test/openpower-pels/Makefile.include
@@ -38,7 +38,7 @@
 	$(top_builddir)/extensions/openpower-pels/failing_mtms.o \
 	$(top_builddir)/extensions/openpower-pels/fru_identity.o \
 	$(top_builddir)/extensions/openpower-pels/generic.o \
-	$(top_builddir)/extensions/openpower-pels/hexdump.o \
+	$(top_builddir)/extensions/openpower-pels/json_utils.o \
 	$(top_builddir)/extensions/openpower-pels/log_id.o \
 	$(top_builddir)/extensions/openpower-pels/mtms.o \
 	$(top_builddir)/extensions/openpower-pels/mru.o \