boot-block: clear block on error resolve
If the blocking error is resolved then remove the blocking object and
it's property change callback
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
Change-Id: I9acb844ef273e8390b4f5283f234990b3c008e70
diff --git a/log_manager.cpp b/log_manager.cpp
index 929ff5d..d636a67 100644
--- a/log_manager.cpp
+++ b/log_manager.cpp
@@ -281,6 +281,39 @@
return false;
}
+void Manager::findAndRemoveResolvedBlocks()
+{
+ for (auto& entry : entries)
+ {
+ if (entry.second->resolved())
+ {
+ checkAndRemoveBlockingError(entry.first);
+ }
+ }
+}
+
+void Manager::onEntryResolve(sdbusplus::message::message& msg)
+{
+ using Interface = std::string;
+ using Property = std::string;
+ using Value = std::string;
+ using Properties = std::map<Property, sdbusplus::message::variant<Value>>;
+
+ Interface interface;
+ Properties properties;
+
+ msg.read(interface, properties);
+
+ for (const auto& p : properties)
+ {
+ if (p.first == "Resolved")
+ {
+ findAndRemoveResolvedBlocks();
+ return;
+ }
+ }
+}
+
void Manager::checkQuiesceOnError(const Entry& entry)
{
@@ -292,16 +325,26 @@
logging::log<logging::level::INFO>(
"QuiesceOnError set and callout present");
- std::string blockPath(OBJ_LOGGING);
- blockPath += "/block";
- blockPath += std::to_string(entry.id());
+ auto blockPath =
+ std::string(OBJ_LOGGING) + "/block" + std::to_string(entry.id());
auto blockObj =
std::make_unique<Block>(this->busLog, blockPath, entry.id());
this->blockingErrors.push_back(std::move(blockObj));
+ // Register call back if log is resolved
+ using namespace sdbusplus::bus::match::rules;
+ auto entryPath = std::string(OBJ_ENTRY) + '/' + std::to_string(entry.id());
+ auto callback = std::make_unique<sdbusplus::bus::match::match>(
+ this->busLog,
+ propertiesChanged(entryPath, "xyz.openbmc_project.Logging.Entry"),
+ std::bind(std::mem_fn(&Manager::onEntryResolve), this,
+ std::placeholders::_1));
+
+ propChangedEntryCallback.insert(
+ std::make_pair(entry.id(), std::move(callback)));
+
// TODO in later commit in this series
// Call systemd to quiesce host
- // Ensure blockingErrors removes entries when log resolved
}
void Manager::doExtensionLogCreate(const Entry& entry, const FFDCEntries& ffdc)
@@ -354,6 +397,7 @@
void Manager::checkAndRemoveBlockingError(uint32_t entryId)
{
+ // First look for blocking object and remove
auto it = find_if(
blockingErrors.begin(), blockingErrors.end(),
[&](std::unique_ptr<Block>& obj) { return obj->entryId == entryId; });
@@ -361,6 +405,14 @@
{
blockingErrors.erase(it);
}
+
+ // Now remove the callback looking for the error to be resolved
+ auto resolveFind = propChangedEntryCallback.find(entryId);
+ if (resolveFind != propChangedEntryCallback.end())
+ {
+ propChangedEntryCallback.erase(resolveFind);
+ }
+
return;
}
diff --git a/log_manager.hpp b/log_manager.hpp
index 700a39a..87dd038 100644
--- a/log_manager.hpp
+++ b/log_manager.hpp
@@ -141,6 +141,15 @@
return blockingErrors.size();
}
+ /** @brief Returns the number of property change callback objects
+ *
+ * @return int - count of property callback entries
+ */
+ int getEntryCallbackSize()
+ {
+ return propChangedEntryCallback.size();
+ }
+
sdbusplus::bus::bus& getBus()
{
return busLog;
@@ -264,6 +273,19 @@
std::vector<std::string> additionalData,
const FFDCEntries& ffdc = FFDCEntries{});
+ /** @brief Notified on entry property changes
+ *
+ * If an entry is blocking, this callback will be registered to monitor for
+ * the entry having it's Resolved field set to true. If it is then remove
+ * the blocking object.
+ *
+ * @param[in] msg - sdbusplus dbusmessage
+ */
+ void onEntryResolve(sdbusplus::message::message& msg);
+
+ /** @brief Remove block objects for any resolved entries */
+ void findAndRemoveResolvedBlocks();
+
/** @brief Persistent sdbusplus DBus bus connection. */
sdbusplus::bus::bus& busLog;
@@ -284,6 +306,10 @@
/** @brief Array of blocking errors */
std::vector<std::unique_ptr<Block>> blockingErrors;
+
+ /** @brief Map of entry id to call back object on properties changed */
+ std::map<uint32_t, std::unique_ptr<sdbusplus::bus::match::match>>
+ propChangedEntryCallback;
};
} // namespace internal
diff --git a/test/elog_quiesce_test.cpp b/test/elog_quiesce_test.cpp
index 1f1cdeb..6537485 100644
--- a/test/elog_quiesce_test.cpp
+++ b/test/elog_quiesce_test.cpp
@@ -158,6 +158,63 @@
EXPECT_EQ(manager.getBlockingErrSize(), 0);
}
+// Test that a blocking error is created on entry with callout
+TEST_F(TestQuiesceOnError, testBlockingErrorsResolved)
+{
+ uint32_t id = 101;
+ uint64_t timestamp{100};
+ std::string message{"test error"};
+ std::string fwLevel{"level42"};
+ std::vector<std::string> testData{
+ "CALLOUT_INVENTORY_PATH=/xyz/openbmc_project/inventory/system/chassis/"
+ "motherboard/powersupply0/"};
+ phosphor::logging::AssociationList associations{};
+
+ // Ensure D-Bus object created for this blocking error
+ // First allow any number of sd_bus_emit_object_added calls
+ EXPECT_CALL(sdbusMock, sd_bus_emit_object_added(testing::_, testing::_))
+ .Times(testing::AnyNumber());
+ // Second verify the new block100 object is created once
+ EXPECT_CALL(sdbusMock,
+ sd_bus_emit_object_added(
+ testing::_, testing::HasSubstr(
+ "/xyz/openbmc_project/logging/block101")))
+ .Times(1);
+
+ Entry elog{mockedBus,
+ std::string(OBJ_ENTRY) + '/' + std::to_string(id),
+ id,
+ timestamp,
+ Entry::Level::Informational,
+ std::move(message),
+ std::move(testData),
+ std::move(associations),
+ fwLevel,
+ manager};
+
+ manager.checkQuiesceOnError(elog);
+ // Created error with callout so expect a blocking error now
+ EXPECT_EQ(manager.getBlockingErrSize(), 1);
+ // Also should have a callback create looking for entry to be resolved
+ EXPECT_EQ(manager.getEntryCallbackSize(), 1);
+
+ // Now resolve the error and make sure the object and entry go away
+ EXPECT_CALL(sdbusMock, sd_bus_emit_object_removed(testing::_, testing::_))
+ .Times(testing::AnyNumber());
+ EXPECT_CALL(sdbusMock,
+ sd_bus_emit_object_removed(
+ testing::_, testing::HasSubstr(
+ "/xyz/openbmc_project/logging/block101")))
+ .Times(1);
+
+ elog.resolved(true);
+ // Note that property signal callbacks do not work in unit test so directly
+ // call the interface to find and resolve blocking entries
+ manager.checkAndRemoveBlockingError(101);
+ EXPECT_EQ(manager.getBlockingErrSize(), 0);
+ EXPECT_EQ(manager.getEntryCallbackSize(), 0);
+}
+
} // namespace test
} // namespace logging
} // namespace phosphor