PEL: Check if PEL pruning necessary on a new PEL

When a new PEL is added check if the size of all PELs is now more than
95% of the maximum capacity.  If it is, call the repository's prune()
function from the event loop to make more space.  Also delete the
OpenBMC event logs corresponding to the PELs that were just deleted.

It's called from the event loop so that the current D-Bus method
response that the code is in now can return to the caller first.

It tops out at 95% to ensure that we never go over.  It's possible this
can be tuned in the future, but at the current 20MB limit that still
allows 19MB before pruning will take it down to at most 18MB.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I962866eceee89cd605fcd36ec08b20ff762fe6cd
diff --git a/extensions/openpower-pels/manager.cpp b/extensions/openpower-pels/manager.cpp
index 66f5ee5..fea2ba9 100644
--- a/extensions/openpower-pels/manager.cpp
+++ b/extensions/openpower-pels/manager.cpp
@@ -120,6 +120,11 @@
                               entry("PEL_ID=0x%X", pel->id()));
 
             _repo.add(pel);
+
+            if (_repo.sizeWarning())
+            {
+                scheduleRepoPrune();
+            }
         }
         catch (std::exception& e)
         {
@@ -281,6 +286,11 @@
 
         _repo.add(pel);
 
+        if (_repo.sizeWarning())
+        {
+            scheduleRepoPrune();
+        }
+
         auto src = pel->primarySRC();
         if (src)
         {
@@ -414,5 +424,25 @@
     }
 }
 
+void Manager::scheduleRepoPrune()
+{
+    sdeventplus::Event event = sdeventplus::Event::get_default();
+
+    _repoPrunerEventSource = std::make_unique<sdeventplus::source::Defer>(
+        event, std::bind(std::mem_fn(&Manager::pruneRepo), this,
+                         std::placeholders::_1));
+}
+
+void Manager::pruneRepo(sdeventplus::source::EventBase& source)
+{
+    auto idsToDelete = _repo.prune();
+
+    // Remove the OpenBMC event logs for the PELs that were just removed.
+    std::for_each(idsToDelete.begin(), idsToDelete.end(),
+                  [this](auto id) { this->_logManager.erase(id); });
+
+    _repoPrunerEventSource.reset();
+}
+
 } // namespace pels
 } // namespace openpower
diff --git a/extensions/openpower-pels/manager.hpp b/extensions/openpower-pels/manager.hpp
index d58b36b..2d443c6 100644
--- a/extensions/openpower-pels/manager.hpp
+++ b/extensions/openpower-pels/manager.hpp
@@ -254,6 +254,23 @@
     PelFFDC convertToPelFFDC(const phosphor::logging::FFDCEntries& ffdc);
 
     /**
+     * @brief Schedules a PEL repository prune to occur from
+     *        the event loop.
+     *
+     * Uses sd_event_add_defer
+     */
+    void scheduleRepoPrune();
+
+    /**
+     * @brief Prunes old PELs out of the repository to save space.
+     *
+     * This is called from the event loop.
+     *
+     * @param[in] source - The event source object used
+     */
+    void pruneRepo(sdeventplus::source::EventBase& source);
+
+    /**
      * @brief Reference to phosphor-logging's Manager class
      */
     phosphor::logging::internal::Manager& _logManager;
@@ -290,6 +307,12 @@
      *        it has been returned from the getPEL D-Bus method.
      */
     std::unique_ptr<sdeventplus::source::Defer> _fdCloserEventSource;
+
+    /**
+     * @brief The even source for removing old PELs when the repo is
+     *        running out of space to make room for new ones.
+     */
+    std::unique_ptr<sdeventplus::source::Defer> _repoPrunerEventSource;
 };
 
 } // namespace pels
diff --git a/extensions/openpower-pels/repository.cpp b/extensions/openpower-pels/repository.cpp
index e608927..59b3eaa 100644
--- a/extensions/openpower-pels/repository.cpp
+++ b/extensions/openpower-pels/repository.cpp
@@ -30,6 +30,8 @@
 using namespace phosphor::logging;
 namespace file_error = sdbusplus::xyz::openbmc_project::Common::File::Error;
 
+constexpr size_t warningPercentage = 95;
+
 /**
  * @brief Returns the amount of space the file uses on disk.
  *
@@ -499,6 +501,12 @@
     }
 }
 
+bool Repository::sizeWarning() const
+{
+    return (_sizes.total > (_maxRepoSize * warningPercentage / 100)) ||
+           (_pelAttributes.size() > _maxNumPELs);
+}
+
 std::vector<Repository::AttributesReference>
     Repository::getAllPELAttributes(SortOrder order) const
 {
diff --git a/extensions/openpower-pels/repository.hpp b/extensions/openpower-pels/repository.hpp
index f33289c..c7cb39a 100644
--- a/extensions/openpower-pels/repository.hpp
+++ b/extensions/openpower-pels/repository.hpp
@@ -357,6 +357,16 @@
     static bool isServiceableSev(const PELAttributes& pel);
 
     /**
+     * @brief Returns true if the total amount of disk space occupied
+     *        by the PELs in the repo is over 95% of the maximum
+     *        size, or if there are over the maximum number of
+     *        PELs allowed.
+     *
+     * @return bool - true if repo is > 95% full or too many PELs
+     */
+    bool sizeWarning() const;
+
+    /**
      * @brief Deletes PELs to bring the repository size down
      *        to at most 90% full by placing PELs into 4 different
      *        catogories and then removing PELs until those catogories