Split BMC dump manager to accomodate more locally stored dumps
The BMC dump manager is having all infrastucure to manage
locally stored dumps. To avoid code duplication while
implementing other locally stored dumps, splitting the
BMC dump manager into locally stored dump manager and
BMC dump manager.
Changes:
Add a new base class bmc_stored::Manager for bmc::Manager
new base class will have following functions
- Store dump properties
- create dump entry based on file watch.
New class diagram will look like this
|-------------|
| base dump |
| manager |
|-------------|
|
|-------------|
| BMC stored |
| dump |
| manager |
|-------------|
|
-------------------------
| |
|---------| |----------|
| BMC | | New |
| dump | | dump |
| manager | | Manager |
|---------| |----------|
Signed-off-by: Dhruvaraj Subhashchandran <dhruvaraj@in.ibm.com>
Change-Id: I02a8766113d030ba2f20bd98cb8713296d01eebe
diff --git a/dump_manager_bmcstored.cpp b/dump_manager_bmcstored.cpp
new file mode 100644
index 0000000..a470960
--- /dev/null
+++ b/dump_manager_bmcstored.cpp
@@ -0,0 +1,220 @@
+#include "config.h"
+
+#include "bmc_dump_entry.hpp"
+#include "dump_internal.hpp"
+#include "dump_manager_bmc.hpp"
+#include "xyz/openbmc_project/Common/error.hpp"
+#include "xyz/openbmc_project/Dump/Create/error.hpp"
+
+#include <fmt/core.h>
+#include <sys/inotify.h>
+#include <unistd.h>
+
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/elog.hpp>
+
+#include <cmath>
+#include <ctime>
+#include <regex>
+
+namespace phosphor
+{
+namespace dump
+{
+namespace bmc_stored
+{
+
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+using namespace phosphor::logging;
+
+void Manager::createEntry(const std::filesystem::path& file)
+{
+ static constexpr auto ID_POS = 1;
+ static constexpr auto EPOCHTIME_POS = 2;
+ std::regex file_regex(dumpFilenameFormat.c_str());
+
+ std::smatch match;
+ std::string name = file.filename();
+
+ if (!((std::regex_search(name, match, file_regex)) && (match.size() > 0)))
+ {
+ log<level::ERR>(fmt::format("Invalid Dump file name, FILENAME({})",
+ file.filename().c_str())
+ .c_str());
+ return;
+ }
+
+ auto idString = match[ID_POS];
+ uint64_t timestamp = stoull(match[EPOCHTIME_POS]) * 1000 * 1000;
+
+ auto id = stoul(idString);
+
+ // If there is an existing entry update it and return.
+ auto dumpEntry = entries.find(id);
+ if (dumpEntry != entries.end())
+ {
+ dynamic_cast<phosphor::dump::bmc_stored::Entry*>(
+ dumpEntry->second.get())
+ ->update(timestamp, std::filesystem::file_size(file), file);
+ return;
+ }
+
+ // Entry Object path.
+ auto objPath = std::filesystem::path(baseEntryPath) / std::to_string(id);
+
+ try
+ {
+ createEntry(id, objPath, timestamp, std::filesystem::file_size(file),
+ file, phosphor::dump::OperationStatus::Completed,
+ std::string(), originatorTypes::Internal);
+ }
+ catch (const InternalFailure& e)
+ {
+ log<level::ERR>(
+ fmt::format(
+ "Error in creating dump entry, errormsg({}), OBJECTPATH({}), "
+ "ID({}), TIMESTAMP({}), SIZE({}), FILENAME({})",
+ e.what(), objPath.c_str(), id, timestamp,
+ std::filesystem::file_size(file), file.filename().c_str())
+ .c_str());
+ return;
+ }
+}
+
+void Manager::watchCallback(const UserMap& fileInfo)
+{
+ for (const auto& i : fileInfo)
+ {
+ // For any new dump file create dump entry object
+ // and associated inotify watch.
+ if (IN_CLOSE_WRITE == i.second)
+ {
+ if (!std::filesystem::is_directory(i.first))
+ {
+ // Don't require filename to be passed, as the path
+ // of dump directory is stored in the childWatchMap
+ removeWatch(i.first.parent_path());
+
+ // dump file is written now create D-Bus entry
+ createEntry(i.first);
+ }
+ else
+ {
+ removeWatch(i.first);
+ }
+ }
+ // Start inotify watch on newly created directory.
+ else if ((IN_CREATE == i.second) &&
+ std::filesystem::is_directory(i.first))
+ {
+ auto watchObj = std::make_unique<Watch>(
+ eventLoop, IN_NONBLOCK, IN_CLOSE_WRITE, EPOLLIN, i.first,
+ std::bind(
+ std::mem_fn(&phosphor::dump::bmc::Manager::watchCallback),
+ this, std::placeholders::_1));
+
+ childWatchMap.emplace(i.first, std::move(watchObj));
+ }
+ }
+}
+
+void Manager::removeWatch(const std::filesystem::path& path)
+{
+ // Delete Watch entry from map.
+ childWatchMap.erase(path);
+}
+
+void Manager::restore()
+{
+ std::filesystem::path dir(dumpDir);
+ if (!std::filesystem::exists(dir) || std::filesystem::is_empty(dir))
+ {
+ return;
+ }
+
+ // Dump file path: <DUMP_PATH>/<id>/<filename>
+ for (const auto& p : std::filesystem::directory_iterator(dir))
+ {
+ auto idStr = p.path().filename().string();
+
+ // Consider only directory's with dump id as name.
+ // Note: As per design one file per directory.
+ if ((std::filesystem::is_directory(p.path())) &&
+ std::all_of(idStr.begin(), idStr.end(), ::isdigit))
+ {
+ lastEntryId =
+ std::max(lastEntryId, static_cast<uint32_t>(std::stoul(idStr)));
+ auto fileIt = std::filesystem::directory_iterator(p.path());
+ // Create dump entry d-bus object.
+ if (fileIt != std::filesystem::end(fileIt))
+ {
+ createEntry(fileIt->path());
+ }
+ }
+ }
+}
+
+size_t getDirectorySize(const std::string dir)
+{
+ auto size = 0;
+ for (const auto& p : std::filesystem::recursive_directory_iterator(dir))
+ {
+ if (!std::filesystem::is_directory(p))
+ {
+ size += std::ceil(std::filesystem::file_size(p) / 1024.0);
+ }
+ }
+ return size;
+}
+
+size_t Manager::getAllowedSize()
+{
+ // Get current size of the dump directory.
+ auto size = getDirectorySize(dumpDir);
+
+ // Set the Dump size to Maximum if the free space is greater than
+ // Dump max size otherwise return the available size.
+
+ size = (size > allocatedSize ? 0 : allocatedSize - size);
+
+#ifdef BMC_DUMP_ROTATE_CONFIG
+ // Delete the first existing file until the space is enough
+ while (size < minDumpSize)
+ {
+ auto delEntry = min_element(
+ entries.begin(), entries.end(),
+ [](const auto& l, const auto& r) { return l.first < r.first; });
+ auto delPath =
+ std::filesystem::path(dumpDir) / std::to_string(delEntry->first);
+
+ size += getDirectorySize(delPath);
+
+ delEntry->second->delete_();
+ }
+#else
+ using namespace sdbusplus::xyz::openbmc_project::Dump::Create::Error;
+ using Reason = xyz::openbmc_project::Dump::Create::QuotaExceeded::REASON;
+
+ if (size < minDumpSize)
+ {
+ log<level::ERR>(fmt::format("Not enough space available({}) miniumum "
+ "needed({}) filled({}) allocated({})",
+ size, minDumpSize,
+ getDirectorySize(dumpDir), allocatedSize)
+ .c_str());
+ // Reached to maximum limit
+ elog<QuotaExceeded>(Reason("Not enough space: Delete old dumps"));
+ }
+#endif
+
+ if (size > maxDumpSize)
+ {
+ size = maxDumpSize;
+ }
+
+ return size;
+}
+
+} // namespace bmc_stored
+} // namespace dump
+} // namespace phosphor