boot-block: create d-bus block object on callout

If the QuiesceOnHwError is set and a callout is present in the Entry
then create the new block object and the
xyz.openbmc_project.Logging.ErrorBlocksTransition interface.

Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
Change-Id: I9aea289ab7cc8dc7649820ff54edc8c04ecb3ef5
diff --git a/elog_block.hpp b/elog_block.hpp
new file mode 100644
index 0000000..e246fa9
--- /dev/null
+++ b/elog_block.hpp
@@ -0,0 +1,45 @@
+#pragma once
+
+#include "xyz/openbmc_project/Logging/ErrorBlocksTransition/server.hpp"
+
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/object.hpp>
+
+namespace phosphor
+{
+namespace logging
+{
+
+using BlockIface = sdbusplus::server::object::object<
+    sdbusplus::xyz::openbmc_project::Logging::server::ErrorBlocksTransition>;
+
+/** @class Block
+ *  @brief OpenBMC logging Block implementation.
+ *  @details A concrete implementation for the
+ *  xyz.openbmc_project.Logging.ErrorBlocksTransition DBus API
+ */
+class Block : public BlockIface
+{
+  public:
+    Block() = delete;
+    Block(const Block&) = delete;
+    Block& operator=(const Block&) = delete;
+    Block(Block&&) = delete;
+    Block& operator=(Block&&) = delete;
+    virtual ~Block() = default;
+
+    /** @brief Constructor to put object onto bus at a dbus path.
+     *  @param[in] bus - Bus to attach to.
+     *  @param[in] path - Path to attach at.
+     *  @param[in] entryId - Distinct ID of the error.
+     */
+    Block(sdbusplus::bus::bus& bus, const std::string& path, uint32_t entryId) :
+        BlockIface(bus, path.c_str()), entryId(entryId){};
+
+    uint32_t entryId;
+
+  private:
+};
+
+} // namespace logging
+} // namespace phosphor
diff --git a/log_manager.cpp b/log_manager.cpp
index cf2a795..72a8f3c 100644
--- a/log_manager.cpp
+++ b/log_manager.cpp
@@ -291,7 +291,17 @@
 
     logging::log<logging::level::INFO>(
         "QuiesceOnError set and callout present");
+
+    std::string blockPath(OBJ_LOGGING);
+    blockPath += "/block";
+    blockPath += std::to_string(entry.id());
+    auto blockObj =
+        std::make_unique<Block>(this->busLog, blockPath, entry.id());
+    this->blockingErrors.push_back(std::move(blockObj));
+
     // 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)
diff --git a/log_manager.hpp b/log_manager.hpp
index b24a7f3..8f8ff82 100644
--- a/log_manager.hpp
+++ b/log_manager.hpp
@@ -1,5 +1,6 @@
 #pragma once
 
+#include "elog_block.hpp"
 #include "elog_entry.hpp"
 #include "xyz/openbmc_project/Collection/DeleteAll/server.hpp"
 #include "xyz/openbmc_project/Logging/Create/server.hpp"
@@ -131,6 +132,15 @@
      */
     int getInfoErrSize();
 
+    /** @brief Returns the number of blocking errors
+     *
+     *  @return int - count of blocking errors
+     */
+    int getBlockingErrSize()
+    {
+        return blockingErrors.size();
+    }
+
     sdbusplus::bus::bus& getBus()
     {
         return busLog;
@@ -265,6 +275,9 @@
 
     /** @brief The BMC firmware version */
     const std::string fwVersion;
+
+    /** @brief Array of blocking errors */
+    std::vector<std::unique_ptr<Block>> blockingErrors;
 };
 
 } // namespace internal
diff --git a/test/elog_quiesce_test.cpp b/test/elog_quiesce_test.cpp
index a29dc3e..3eb2a3d 100644
--- a/test/elog_quiesce_test.cpp
+++ b/test/elog_quiesce_test.cpp
@@ -78,6 +78,70 @@
     EXPECT_EQ(manager.isCalloutPresent(elog), true);
 }
 
+// Test that no blocking errors are created when no callout
+TEST_F(TestQuiesceOnError, testNoBlockingErrorsCreated)
+{
+    uint32_t id = 99;
+    uint64_t timestamp{100};
+    std::string message{"test error"};
+    std::string fwLevel{"level42"};
+    std::vector<std::string> testData{"no", "callout"};
+    phosphor::logging::AssociationList associations{};
+
+    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);
+    EXPECT_EQ(manager.getBlockingErrSize(), 0);
+}
+
+// Test that a blocking error is created on entry with callout
+TEST_F(TestQuiesceOnError, testBlockingErrorsCreated)
+{
+    uint32_t id = 100;
+    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/block100")))
+        .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);
+}
+
 } // namespace test
 } // namespace logging
 } // namespace phosphor