OpenPOWER: Allow initiating mp reboot dump in quiesced state

When system dumps are disabled, a system dump collection
request will end up in quiesced state.  A memory preserving
reboot from that state can get the failed data from the host.
So enabling the mp reboot dump collection from quiesced state.

Test:
Disable dump
Inject error in host to move to quiesced state
Start mp reboot
Dump should be generated

Signed-off-by: Dhruvaraj Subhashchandran <dhruvaraj@in.ibm.com>
Change-Id: I9f444752b321f1ab47b99e5b8ac32c79182d6453
diff --git a/dump-extensions/openpower-dumps/dump_manager_system.cpp b/dump-extensions/openpower-dumps/dump_manager_system.cpp
index 949c87c..e828371 100644
--- a/dump-extensions/openpower-dumps/dump_manager_system.cpp
+++ b/dump-extensions/openpower-dumps/dump_manager_system.cpp
@@ -154,11 +154,30 @@
         sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
     using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
 
-    // Allow creating system dump only when the host is up.
-    if (!phosphor::dump::isHostRunning())
+    auto isHostRunning = false;
+    phosphor::dump::HostState hostState;
+    try
     {
+        isHostRunning = phosphor::dump::isHostRunning();
+        hostState = phosphor::dump::getHostState();
+    }
+    catch (const std::exception& e)
+    {
+        lg2::error(
+            "System state cannot be determined, system dump is not allowed: "
+            "{ERROR}",
+            "ERROR", e);
+        return std::string();
+    }
+    bool isHostQuiesced = hostState == phosphor::dump::HostState::Quiesced;
+    // Allow creating system dump only when the host is up or quiesced
+    if (!isHostRunning && !isHostQuiesced)
+    {
+        lg2::error("System dump can be initiated only when the host is up "
+                   "or quiesced");
         elog<NotAllowed>(
-            Reason("System dump can be initiated only when the host is up"));
+            Reason("System dump can be initiated only when the host is up "
+                   "or quiesced"));
         return std::string();
     }
 
diff --git a/dump_utils.cpp b/dump_utils.cpp
index 74246ae..792138c 100644
--- a/dump_utils.cpp
+++ b/dump_utils.cpp
@@ -39,81 +39,10 @@
         lg2::error("Error in mapper method call, errormsg: {ERROR}, "
                    "PATH: {PATH}, INTERFACE: {INTERFACE}",
                    "ERROR", e, "PATH", path, "INTERFACE", interface);
-        return std::string{};
+        throw;
     }
     return response[0].first;
 }
 
-BootProgress getBootProgress()
-{
-    constexpr auto bootProgressInterface =
-        "xyz.openbmc_project.State.Boot.Progress";
-    // TODO Need to change host instance if multiple instead "0"
-    constexpr auto hostStateObjPath = "/xyz/openbmc_project/state/host0";
-
-    BootProgress bootProgessStage;
-
-    try
-    {
-        auto bus = sdbusplus::bus::new_default();
-        auto service = getService(bus, hostStateObjPath, bootProgressInterface);
-
-        auto method = bus.new_method_call(service.c_str(), hostStateObjPath,
-                                          "org.freedesktop.DBus.Properties",
-                                          "Get");
-
-        method.append(bootProgressInterface, "BootProgress");
-
-        auto reply = bus.call(method);
-
-        using DBusValue_t =
-            std::variant<std::string, bool, std::vector<uint8_t>,
-                         std::vector<std::string>>;
-        DBusValue_t propertyVal;
-
-        reply.read(propertyVal);
-
-        // BootProgress property type is string
-        std::string bootPgs(std::get<std::string>(propertyVal));
-
-        bootProgessStage = sdbusplus::xyz::openbmc_project::State::Boot::
-            server::Progress::convertProgressStagesFromString(bootPgs);
-    }
-    catch (const sdbusplus::exception_t& e)
-    {
-        lg2::error("D-Bus call exception, OBJPATH: {OBJ_PATH}, "
-                   "INTERFACE: {INTERFACE}, EXCEPTION: {ERROR}",
-                   "OBJ_PATH", hostStateObjPath, "INTERFACE",
-                   bootProgressInterface, "ERROR", e);
-        throw std::runtime_error("Failed to get BootProgress stage");
-    }
-    catch (const std::bad_variant_access& e)
-    {
-        lg2::error("Exception raised while read BootProgress property value, "
-                   "OBJPATH: {OBJ_PATH}, INTERFACE: {INTERFACE}, "
-                   "EXCEPTION: {ERROR}",
-                   "OBJ_PATH", hostStateObjPath, "INTERFACE",
-                   bootProgressInterface, "ERROR", e);
-        throw std::runtime_error("Failed to get BootProgress stage");
-    }
-
-    return bootProgessStage;
-}
-
-bool isHostRunning()
-{
-    // TODO #ibm-openbmc/dev/2858 Revisit the method for finding whether host
-    // is running.
-    BootProgress bootProgressStatus = phosphor::dump::getBootProgress();
-    if ((bootProgressStatus == BootProgress::SystemInitComplete) ||
-        (bootProgressStatus == BootProgress::SystemSetup) ||
-        (bootProgressStatus == BootProgress::OSStart) ||
-        (bootProgressStatus == BootProgress::OSRunning) ||
-        (bootProgressStatus == BootProgress::PCIInit))
-    {
-        return true;
-    }
-    return false;
-}
 } // namespace dump
 } // namespace phosphor
diff --git a/dump_utils.hpp b/dump_utils.hpp
index 6e923bc..6d39bef 100644
--- a/dump_utils.hpp
+++ b/dump_utils.hpp
@@ -11,6 +11,7 @@
 #include <xyz/openbmc_project/Common/error.hpp>
 #include <xyz/openbmc_project/Dump/Create/server.hpp>
 #include <xyz/openbmc_project/State/Boot/Progress/server.hpp>
+#include <xyz/openbmc_project/State/Host/server.hpp>
 
 #include <memory>
 
@@ -21,6 +22,8 @@
 
 using BootProgress = sdbusplus::xyz::openbmc_project::State::Boot::server::
     Progress::ProgressStages;
+using HostState =
+    sdbusplus::xyz::openbmc_project::State::server::Host::HostState;
 
 using namespace phosphor::logging;
 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
@@ -79,26 +82,149 @@
  * @param[in] path - D-Bus path name.
  * @param[in] interface - D-Bus interface name.
  * @return the bus service as a string
+ *
+ * @throws sdbusplus::exception::SdBusError - If any D-Bus error occurs during
+ * the call.
  **/
 std::string getService(sdbusplus::bus_t& bus, const std::string& path,
                        const std::string& interface);
 
 /**
+ * @brief Read property value from the specified object and interface
+ * @param[in] bus D-Bus handle
+ * @param[in] service service which has implemented the interface
+ * @param[in] object object having has implemented the interface
+ * @param[in] intf interface having the property
+ * @param[in] prop name of the property to read
+ * @throws sdbusplus::exception::SdBusError if an error occurs in the dbus call
+ * @return property value
+ */
+template <typename T>
+T readDBusProperty(sdbusplus::bus_t& bus, const std::string& service,
+                   const std::string& object, const std::string& intf,
+                   const std::string& prop)
+{
+    T retVal{};
+    try
+    {
+        auto properties = bus.new_method_call(service.c_str(), object.c_str(),
+                                              "org.freedesktop.DBus.Properties",
+                                              "Get");
+        properties.append(intf);
+        properties.append(prop);
+        auto result = bus.call(properties);
+        result.read(retVal);
+    }
+    catch (const std::exception& ex)
+    {
+        lg2::error(
+            "Failed to get the property: {PROPERTY} interface: {INTERFACE} "
+            "object path: {OBJECT_PATH} error: {ERROR} ",
+            "PROPERTY", prop, "INTERFACE", intf, "OBJECT_PATH", object, "ERROR",
+            ex);
+        throw;
+    }
+    return retVal;
+}
+
+/**
+ * @brief Get the state value
+ *
+ * @param[in] intf - Interface to get the value
+ * @param[in] objPath - Object path of the service
+ * @param[in] state - State name to get
+ *
+ * @return The state value as type T on successful retrieval.
+ *
+ * @throws sdbusplus::exception for D-Bus failures and std::bad_variant_access
+ * for invalid value
+ */
+template <typename T>
+T getStateValue(const std::string& intf, const std::string& objPath,
+                const std::string& state)
+{
+    try
+    {
+        auto bus = sdbusplus::bus::new_default();
+        auto service = getService(bus, objPath, intf);
+        return std::get<T>(readDBusProperty<std::variant<T>>(
+            bus, service, objPath, intf, state));
+    }
+    catch (const sdbusplus::exception_t& e)
+    {
+        lg2::error(
+            "D-Bus call exception, OBJPATH: {OBJPATH}, "
+            "INTERFACE: {INTERFACE}, PROPERTY: {PROPERTY}, error: {ERROR}",
+            "OBJPATH", objPath, "INTERFACE", intf, "PROPERTY", state, "ERROR",
+            e);
+        throw;
+    }
+    catch (const std::bad_variant_access& e)
+    {
+        lg2::error("Exception raised while read state: {STATE} property "
+                   "value,  OBJPATH: {OBJPATH}, INTERFACE: {INTERFACE}, "
+                   "error: {ERROR}",
+                   "STATE", state, "OBJPATH", objPath, "INTERFACE", intf,
+                   "ERROR", e);
+        throw;
+    }
+}
+
+/**
+ * @brief Get the host state
+ *
+ * @return HostState on success
+ *
+ * @throws std::runtime_error - If getting the state property fails
+ */
+inline HostState getHostState()
+{
+    constexpr auto hostStateInterface = "xyz.openbmc_project.State.Host";
+    // TODO Need to change host instance if multiple instead "0"
+    constexpr auto hostStateObjPath = "/xyz/openbmc_project/state/host0";
+    return getStateValue<HostState>(hostStateInterface, hostStateObjPath,
+                                    "CurrentHostState");
+}
+
+/**
  * @brief Get the host boot progress stage
  *
  * @return BootProgress on success
- *         Throw exception on failure
  *
+ * @throws std::runtime_error - If getting the state property fails
  */
-BootProgress getBootProgress();
+inline BootProgress getBootProgress()
+{
+    constexpr auto bootProgressInterface =
+        "xyz.openbmc_project.State.Boot.Progress";
+    // TODO Need to change host instance if multiple instead "0"
+    constexpr auto hostStateObjPath = "/xyz/openbmc_project/state/host0";
+    return getStateValue<BootProgress>(bootProgressInterface, hostStateObjPath,
+                                       "BootProgress");
+}
 
 /**
  * @brief Check whether host is running
  *
  * @return true if the host running else false.
- *         Throw exception on failure.
+ *
+ * @throws std::runtime_error - If getting the boot progress failed
  */
-bool isHostRunning();
+inline bool isHostRunning()
+{
+    // TODO #ibm-openbmc/dev/2858 Revisit the method for finding whether host
+    // is running.
+    BootProgress bootProgressStatus = getBootProgress();
+    if ((bootProgressStatus == BootProgress::SystemInitComplete) ||
+        (bootProgressStatus == BootProgress::SystemSetup) ||
+        (bootProgressStatus == BootProgress::OSStart) ||
+        (bootProgressStatus == BootProgress::OSRunning) ||
+        (bootProgressStatus == BootProgress::PCIInit))
+    {
+        return true;
+    }
+    return false;
+}
 
 inline void extractOriginatorProperties(phosphor::dump::DumpCreateParams params,
                                         std::string& originatorId,
@@ -162,5 +288,17 @@
     }
 }
 
+/**
+ * @brief Check whether host is quiesced
+ *
+ * @return true if the host is quiesced else false.
+ *
+ * @throws std::runtime_error - If getting the state failed
+ */
+inline bool isHostQuiesced()
+{
+    return (getHostState() == HostState::Quiesced);
+}
+
 } // namespace dump
 } // namespace phosphor