hypervisor: monitor boot progress

The BootProgress property will be tracked to set the hypervisor state as
appropriate

Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
Change-Id: I5aa5d95893222169bc5abed6b851e795f2ad5ff0
diff --git a/hypervisor_state_manager.cpp b/hypervisor_state_manager.cpp
index acfbb83..afd496a 100644
--- a/hypervisor_state_manager.cpp
+++ b/hypervisor_state_manager.cpp
@@ -46,9 +46,6 @@
     return server::Host::requestedHostTransition(value);
 }
 
-// TODO - Monitor BootProgress and update hypervisor state to Running if
-//        OS is started
-
 server::Host::HostState Hypervisor::currentHostState(HostState value)
 {
     log<level::INFO>(
@@ -57,6 +54,54 @@
     return server::Host::currentHostState(value);
 }
 
+server::Host::HostState Hypervisor::currentHostState()
+{
+    return server::Host::currentHostState();
+}
+
+void Hypervisor::updateCurrentHostState(std::string& bootProgress)
+{
+    log<level::DEBUG>(
+        fmt::format("New BootProgress: {}", bootProgress).c_str());
+
+    if (bootProgress == "xyz.openbmc_project.State.Boot.Progress."
+                        "ProgressStages.SystemInitComplete")
+    {
+        currentHostState(server::Host::HostState::Standby);
+    }
+    else if (bootProgress == "xyz.openbmc_project.State.Boot.Progress."
+                             "ProgressStages.OSStart")
+    {
+        currentHostState(server::Host::HostState::TransitioningToRunning);
+    }
+    else if (bootProgress == "xyz.openbmc_project.State.Boot.Progress."
+                             "ProgressStages.OSRunning")
+    {
+        currentHostState(server::Host::HostState::Running);
+    }
+    else
+    {
+        // BootProgress changed and it is not one of the above so
+        // set hypervisor state to off
+        currentHostState(server::Host::HostState::Off);
+    }
+}
+
+void Hypervisor::bootProgressChangeEvent(sdbusplus::message::message& msg)
+{
+    std::string statusInterface;
+    std::map<std::string, std::variant<std::string>> msgData;
+    msg.read(statusInterface, msgData);
+
+    auto propertyMap = msgData.find("BootProgress");
+    if (propertyMap != msgData.end())
+    {
+        // Extract the BootProgress
+        auto& bootProgress = std::get<std::string>(propertyMap->second);
+        updateCurrentHostState(bootProgress);
+    }
+}
+
 } // namespace manager
 } // namespace state
 } // namespace phosphor
diff --git a/hypervisor_state_manager.hpp b/hypervisor_state_manager.hpp
index b0ce297..9bb3cb2 100644
--- a/hypervisor_state_manager.hpp
+++ b/hypervisor_state_manager.hpp
@@ -18,6 +18,7 @@
     sdbusplus::xyz::openbmc_project::State::server::Host>;
 
 namespace server = sdbusplus::xyz::openbmc_project::State::server;
+namespace sdbusRule = sdbusplus::bus::match::rules;
 
 /** @class Host
  *  @brief OpenBMC host state management implementation.
@@ -40,7 +41,14 @@
      * @param[in] objPath   - The Dbus object path
      */
     Hypervisor(sdbusplus::bus::bus& bus, const char* objPath) :
-        HypervisorInherit(bus, objPath, false), bus(bus)
+        HypervisorInherit(bus, objPath, false), bus(bus),
+        bootProgressChangeSignal(
+            bus,
+            sdbusRule::propertiesChanged(
+                "/xyz/openbmc_project/state/host0",
+                "xyz.openbmc_project.State.Boot.Progress"),
+            std::bind(std::mem_fn(&Hypervisor::bootProgressChangeEvent), this,
+                      std::placeholders::_1))
     {}
 
     /** @brief Set value of HostTransition */
@@ -51,9 +59,32 @@
     server::Host::HostState
         currentHostState(server::Host::HostState value) override;
 
+    /** @brief Return value of CurrentHostState */
+    server::Host::HostState currentHostState();
+
+    /** @brief Check if BootProgress change affects hypervisor state
+     *
+     * @param[in]  bootProgress     - BootProgress value to check
+     *
+     */
+    void updateCurrentHostState(std::string& bootProgress);
+
   private:
+    /** @brief Process BootProgress property changes
+     *
+     * Instance specific interface to monitor for changes to the BootProgress
+     * property which may impact Hypervisor state.
+     *
+     * @param[in]  msg              - Data associated with subscribed signal
+     *
+     */
+    void bootProgressChangeEvent(sdbusplus::message::message& msg);
+
     /** @brief Persistent sdbusplus DBus bus connection. */
     sdbusplus::bus::bus& bus;
+
+    /** @brief Watch BootProgress changes to know hypervisor state **/
+    sdbusplus::bus::match_t bootProgressChangeSignal;
 };
 
 } // namespace manager
diff --git a/meson.build b/meson.build
index 170971a..53df188 100644
--- a/meson.build
+++ b/meson.build
@@ -208,4 +208,17 @@
           include_directories: '../'
       )
   )
+
+  test(
+      'test_hypervisor_state',
+      executable('test_hypervisor_state',
+          './test/hypervisor_state.cpp',
+          'hypervisor_state_manager.cpp',
+          dependencies: [
+              gtest, sdbusplus, sdeventplus, phosphorlogging,
+          ],
+          implicit_include_directories: true,
+          include_directories: '../'
+      )
+  )
 endif
diff --git a/test/hypervisor_state.cpp b/test/hypervisor_state.cpp
new file mode 100644
index 0000000..a6ee783
--- /dev/null
+++ b/test/hypervisor_state.cpp
@@ -0,0 +1,39 @@
+#include "config.h"
+
+#include <hypervisor_state_manager.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdeventplus/event.hpp>
+
+#include <gtest/gtest.h>
+
+namespace server = sdbusplus::xyz::openbmc_project::State::server;
+
+TEST(updateCurrentHostState, BasicPaths)
+{
+    auto bus = sdbusplus::bus::new_default();
+    auto event = sdeventplus::Event::get_default();
+    bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
+    auto objPathInst = std::string{HYPERVISOR_OBJPATH} + '0';
+
+    phosphor::state::manager::Hypervisor hypObj(bus, objPathInst.c_str());
+
+    std::string bootProgress = "Invalid.Boot.Progress";
+    hypObj.updateCurrentHostState(bootProgress);
+    EXPECT_EQ(hypObj.currentHostState(), server::Host::HostState::Off);
+
+    bootProgress = "xyz.openbmc_project.State.Boot.Progress."
+                   "ProgressStages.SystemInitComplete";
+    hypObj.updateCurrentHostState(bootProgress);
+    EXPECT_EQ(hypObj.currentHostState(), server::Host::HostState::Standby);
+
+    bootProgress = "xyz.openbmc_project.State.Boot.Progress."
+                   "ProgressStages.OSStart";
+    hypObj.updateCurrentHostState(bootProgress);
+    EXPECT_EQ(hypObj.currentHostState(),
+              server::Host::HostState::TransitioningToRunning);
+
+    bootProgress = "xyz.openbmc_project.State.Boot.Progress."
+                   "ProgressStages.OSRunning";
+    hypObj.updateCurrentHostState(bootProgress);
+    EXPECT_EQ(hypObj.currentHostState(), server::Host::HostState::Running);
+}