PEL: Deleted PELs moved to new folder under logs

 - PELs whose corresponding event logs have been deleted
   will be available in the archive folder.
 - Archive folder size is tracked under sizeWarning() function.
 - Archived PELs log can be viewed using peltool with flag --archive.
 - PELs deleted using peltool is not archived.
 - Updated README.md

Change-Id: Ie2c1b4c2ca30fb79904bc9d582a01ef8102aed0e
Signed-off-by: Sumit Kumar <sumit_kumar@in.ibm.com>
diff --git a/extensions/openpower-pels/README.md b/extensions/openpower-pels/README.md
index 9c13374..637a389 100644
--- a/extensions/openpower-pels/README.md
+++ b/extensions/openpower-pels/README.md
@@ -686,4 +686,18 @@
 Note: "phal" build-time configure option should be "enabled" to enable this
        feature.
 
+## PEL Archiving
+
+When an OpenBMC event log is deleted its corresponding PEL is moved to
+an archive folder. These archived PELs will be available in BMC dump.
+The archive path: /var/lib/phosphor-logging/extensions/pels/logs/archive.
+
+Highlighted points are:
+- PELs whose corresponding event logs have been deleted will be available
+  in the archive folder.
+- Archive folder size is tracked along with logs folder size and if
+  combined size exceeds warning size all archived PELs will be deleted.
+- Archived PEL logs can be viewed using peltool with flag --archive.
+- If a PEL is deleted using peltool its not archived.
+
 [1]: https://github.com/openbmc/docs/blob/master/designs/fail-boot-on-hw-error.md
diff --git a/extensions/openpower-pels/repository.cpp b/extensions/openpower-pels/repository.cpp
index fd5c238..f552a75 100644
--- a/extensions/openpower-pels/repository.cpp
+++ b/extensions/openpower-pels/repository.cpp
@@ -61,13 +61,19 @@
 Repository::Repository(const std::filesystem::path& basePath, size_t repoSize,
                        size_t maxNumPELs) :
     _logPath(basePath / "logs"),
-    _maxRepoSize(repoSize), _maxNumPELs(maxNumPELs)
+    _maxRepoSize(repoSize), _maxNumPELs(maxNumPELs),
+    _archivePath(basePath / "logs" / "archive")
 {
     if (!fs::exists(_logPath))
     {
         fs::create_directories(_logPath);
     }
 
+    if (!fs::exists(_archivePath))
+    {
+        fs::create_directories(_archivePath);
+    }
+
     restore();
 }
 
@@ -138,6 +144,12 @@
                             entry("ERROR=%s", e.what()));
         }
     }
+
+    // Get size of archive folder
+    for (auto& dirEntry : fs::directory_iterator(_archivePath))
+    {
+        _archiveSize += getFileDiskSize(dirEntry);
+    }
 }
 
 std::string Repository::getPELFilename(uint32_t pelID, const BCDTime& time)
@@ -223,7 +235,23 @@
     log<level::DEBUG>("Removing PEL from repository",
                       entry("PEL_ID=0x%X", actualID.pelID.id),
                       entry("OBMC_LOG_ID=%d", actualID.obmcID.id));
-    fs::remove(pel->second.path);
+
+    if (fs::exists(pel->second.path))
+    {
+        // Check for existense of new archive folder
+        if (!fs::exists(_archivePath))
+        {
+            fs::create_directories(_archivePath);
+        }
+
+        // Move log file to archive folder
+        auto fileName = _archivePath / pel->second.path.filename();
+        fs::rename(pel->second.path, fileName);
+
+        // Update size of file
+        _archiveSize += getFileDiskSize(fileName);
+    }
+
     _pelAttributes.erase(pel);
 
     processDeleteCallbacks(actualID.pelID.id);
@@ -504,6 +532,21 @@
 
 bool Repository::sizeWarning() const
 {
+    if ((_archiveSize > 0) && ((_sizes.total + _archiveSize) >
+                               ((_maxRepoSize * warningPercentage) / 100)))
+    {
+        log<level::INFO>(
+            "Repository::sizeWarning function:Deleting the files in archive");
+
+        std::string cmd = "rm " + _archivePath.string() + "/*_*";
+        auto rc = system(cmd.c_str());
+        if (rc)
+        {
+            log<level::ERR>("Repository::sizeWarning function:Could not delete "
+                            "files in archive");
+        }
+    }
+
     return (_sizes.total > (_maxRepoSize * warningPercentage / 100)) ||
            (_pelAttributes.size() > _maxNumPELs);
 }
diff --git a/extensions/openpower-pels/repository.hpp b/extensions/openpower-pels/repository.hpp
index e2f497b..31c569c 100644
--- a/extensions/openpower-pels/repository.hpp
+++ b/extensions/openpower-pels/repository.hpp
@@ -575,6 +575,16 @@
      * @brief The ID of the most recently added PEL.
      */
     uint32_t _lastPelID = 0;
+
+    /**
+     * @brief The filesystem path to the archive PEL logs.
+     */
+    const std::filesystem::path _archivePath;
+
+    /**
+     * @brief The size of archive folder.
+     */
+    uint64_t _archiveSize = 0;
 };
 
 } // namespace pels
diff --git a/extensions/openpower-pels/tools/peltool.cpp b/extensions/openpower-pels/tools/peltool.cpp
index fcea8af..6dc2ef0 100644
--- a/extensions/openpower-pels/tools/peltool.cpp
+++ b/extensions/openpower-pels/tools/peltool.cpp
@@ -289,7 +289,8 @@
 std::string genPELJSON(T itr, bool hidden, bool includeInfo, bool critSysTerm,
                        bool fullPEL, bool& foundPEL,
                        const std::optional<std::regex>& scrubRegex,
-                       const std::vector<std::string>& plugins, bool hexDump)
+                       const std::vector<std::string>& plugins, bool hexDump,
+                       bool archive)
 {
     std::size_t found;
     std::string val;
@@ -300,7 +301,7 @@
             itr.second.yearLSB, itr.second.month, itr.second.day,
             itr.second.hour, itr.second.minutes, itr.second.seconds,
             itr.second.hundredths, itr.first);
-    auto fileName = pelLogDir() + name;
+    auto fileName = (archive ? pelLogDir() + "/archive" : pelLogDir()) + name;
     try
     {
         std::vector<uint8_t> data = getFileData(fileName);
@@ -450,13 +451,14 @@
  */
 void printPELs(bool order, bool hidden, bool includeInfo, bool critSysTerm,
                bool fullPEL, const std::optional<std::regex>& scrubRegex,
-               bool hexDump)
+               bool hexDump, bool archive = false)
 {
     std::string listStr;
     std::map<uint32_t, BCDTime> PELs;
     std::vector<std::string> plugins;
     listStr = "{\n";
-    for (auto it = fs::directory_iterator(pelLogDir());
+    for (auto it = (archive ? fs::directory_iterator(pelLogDir() + "/archive")
+                            : fs::directory_iterator(pelLogDir()));
          it != fs::directory_iterator(); ++it)
     {
         if (!fs::is_regular_file((*it).path()))
@@ -469,6 +471,7 @@
                          fileNameToTimestamp((*it).path().filename()));
         }
     }
+
     bool foundPEL = false;
 
     if (fullPEL && !hexDump)
@@ -476,10 +479,10 @@
         plugins = getPlugins();
     }
     auto buildJSON = [&listStr, &hidden, &includeInfo, &critSysTerm, &fullPEL,
-                      &foundPEL, &scrubRegex, &plugins,
-                      &hexDump](const auto& i) {
+                      &foundPEL, &scrubRegex, &plugins, &hexDump,
+                      &archive](const auto& i) {
         listStr += genPELJSON(i, hidden, includeInfo, critSysTerm, fullPEL,
-                              foundPEL, scrubRegex, plugins, hexDump);
+                              foundPEL, scrubRegex, plugins, hexDump, archive);
     };
     if (order)
     {
@@ -530,7 +533,8 @@
  * @param[in] hexDump - Boolean to print hexdump of PEL instead of JSON
  */
 void callFunctionOnPEL(const std::string& id, const PELFunc& func,
-                       bool useBMC = false, bool hexDump = false)
+                       bool useBMC = false, bool hexDump = false,
+                       bool archive = false)
 {
     std::string pelID{id};
     if (!useBMC)
@@ -545,7 +549,8 @@
 
     bool found = false;
 
-    for (auto it = fs::directory_iterator(pelLogDir());
+    for (auto it = (archive ? fs::directory_iterator(pelLogDir() + "/archive")
+                            : fs::directory_iterator(pelLogDir()));
          it != fs::directory_iterator(); ++it)
     {
         // The PEL ID is part of the filename, so use that to find the PEL if
@@ -629,6 +634,10 @@
 
     for (const auto& entry : fs::directory_iterator(pelLogDir()))
     {
+        if (!fs::is_regular_file(entry.path()))
+        {
+            continue;
+        }
         fs::remove(entry.path());
     }
 }
@@ -820,6 +829,7 @@
     bool showPELCount = false;
     bool fullPEL = false;
     bool hexDump = false;
+    bool archive = false;
 
     app.set_help_flag("--help", "Print this help message and exit");
     app.add_option("--file", fileName, "Display a PEL using its Raw PEL file");
@@ -839,6 +849,7 @@
     app.add_option("-s, --scrub", scrubFile,
                    "File containing SRC regular expressions to ignore");
     app.add_flag("-x", hexDump, "Display PEL(s) in hexdump instead of JSON");
+    app.add_flag("--archive", archive, "List or display archived PELs");
 
     CLI11_PARSE(app, argc, argv);
 
@@ -868,11 +879,11 @@
     }
     else if (!idPEL.empty())
     {
-        callFunctionOnPEL(idPEL, displayPEL, false, hexDump);
+        callFunctionOnPEL(idPEL, displayPEL, false, hexDump, archive);
     }
     else if (!bmcId.empty())
     {
-        callFunctionOnPEL(bmcId, displayPEL, true, hexDump);
+        callFunctionOnPEL(bmcId, displayPEL, true, hexDump, archive);
     }
     else if (fullPEL || listPEL)
     {
@@ -881,7 +892,7 @@
             scrubRegex = genRegex(scrubFile);
         }
         printPELs(listPELDescOrd, hidden, includeInfo, critSysTerm, fullPEL,
-                  scrubRegex, hexDump);
+                  scrubRegex, hexDump, archive);
     }
     else if (showPELCount)
     {