common: host power utility
Create this utility class to abstract host power operation with a single
function call.
This was refactored out of [1] (BIOS Code Updater)
Tested: Has been tested as part of [1]
References:
[1] https://gerrit.openbmc.org/c/openbmc/phosphor-bmc-code-mgmt/+/76101
Change-Id: I97dc8b1824f70f0aeede3b39683c2ee4ef9ca3c9
Signed-off-by: Alexander Hansen <alexander.hansen@9elements.com>
diff --git a/common/include/host_power.hpp b/common/include/host_power.hpp
new file mode 100644
index 0000000..90cfff1
--- /dev/null
+++ b/common/include/host_power.hpp
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <sdbusplus/async/context.hpp>
+#include <sdbusplus/async/match.hpp>
+#include <xyz/openbmc_project/State/Host/client.hpp>
+
+namespace phosphor::software::host_power
+{
+
+const auto stateOn =
+ sdbusplus::client::xyz::openbmc_project::state::Host<>::HostState::Running;
+const auto stateOff =
+ sdbusplus::client::xyz::openbmc_project::state::Host<>::HostState::Off;
+
+using HostState =
+ sdbusplus::client::xyz::openbmc_project::state::Host<>::HostState;
+
+class HostPower
+{
+ public:
+ HostPower(sdbusplus::async::context& ctx);
+
+ // @param state desired powerstate
+ // @returns true on success
+ static sdbusplus::async::task<bool> setState(sdbusplus::async::context& ctx,
+ HostState state);
+
+ // @returns host powerstate
+ static sdbusplus::async::task<HostState> getState(
+ sdbusplus::async::context& ctx);
+
+ sdbusplus::async::match stateChangedMatch;
+};
+
+}; // namespace phosphor::software::host_power
diff --git a/common/meson.build b/common/meson.build
index 73ef76f..1e29ae8 100644
--- a/common/meson.build
+++ b/common/meson.build
@@ -8,6 +8,7 @@
'src/software_config.cpp',
'src/software.cpp',
'src/software_update.cpp',
+ 'src/host_power.cpp',
include_directories: ['.', 'include/', common_include],
dependencies: [pdi_dep, phosphor_logging_dep, sdbusplus_dep, libpldm_dep],
)
diff --git a/common/src/host_power.cpp b/common/src/host_power.cpp
new file mode 100644
index 0000000..d6a78a1
--- /dev/null
+++ b/common/src/host_power.cpp
@@ -0,0 +1,98 @@
+#include "host_power.hpp"
+
+#include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/async.hpp>
+#include <sdbusplus/async/context.hpp>
+#include <sdbusplus/async/match.hpp>
+#include <sdbusplus/async/proxy.hpp>
+#include <sdbusplus/bus/match.hpp>
+#include <sdbusplus/message/native_types.hpp>
+#include <xyz/openbmc_project/ObjectMapper/client.hpp>
+#include <xyz/openbmc_project/State/Host/client.hpp>
+
+PHOSPHOR_LOG2_USING;
+
+using namespace std::literals;
+
+namespace RulesIntf = sdbusplus::bus::match::rules;
+
+using StateIntf =
+ sdbusplus::client::xyz::openbmc_project::state::Host<void, void>;
+
+const auto transitionOn =
+ sdbusplus::client::xyz::openbmc_project::state::Host<>::Transition::On;
+const auto transitionOff =
+ sdbusplus::client::xyz::openbmc_project::state::Host<>::Transition::Off;
+
+namespace phosphor::software::host_power
+{
+
+constexpr const char* host0ObjectPath = "/xyz/openbmc_project/state/host0";
+constexpr const char* service = "xyz.openbmc_project.State.Host";
+
+HostPower::HostPower(sdbusplus::async::context& ctx) :
+ stateChangedMatch(ctx, RulesIntf::propertiesChanged(host0ObjectPath,
+ StateIntf::interface))
+{}
+
+// NOLINTBEGIN(readability-static-accessed-through-instance)
+sdbusplus::async::task<bool> HostPower::setState(sdbusplus::async::context& ctx,
+ HostState state)
+// NOLINTEND(readability-static-accessed-through-instance)
+{
+ if (state != stateOn && state != stateOff)
+ {
+ error("Invalid power state {STATE}", "STATE", state);
+ co_return false;
+ }
+
+ auto client = sdbusplus::client::xyz::openbmc_project::state::Host(ctx)
+ .service(service)
+ .path(host0ObjectPath);
+
+ co_await client.requested_host_transition(
+ (state == stateOn) ? transitionOn : transitionOff);
+
+ debug("Requested host transition to {STATE}", "STATE", state);
+
+ constexpr size_t retries = 4;
+ constexpr size_t retryTimeout = 3;
+
+ for (size_t i = 0; i < retries; i++)
+ {
+ co_await sdbusplus::async::sleep_for(
+ ctx, std::chrono::seconds(retryTimeout));
+
+ if ((co_await client.current_host_state()) == state)
+ {
+ debug("Successfully achieved state {STATE}", "STATE", state);
+ co_return true;
+ }
+ }
+
+ error("Failed to achieve state {STATE} before the timeout of {TIMEOUT}s",
+ "STATE", state, "TIMEOUT", retries * retryTimeout);
+
+ co_return false;
+}
+
+// NOLINTBEGIN(readability-static-accessed-through-instance)
+sdbusplus::async::task<HostState> HostPower::getState(
+ sdbusplus::async::context& ctx)
+// NOLINTEND(readability-static-accessed-through-instance)
+{
+ auto client = sdbusplus::client::xyz::openbmc_project::state::Host(ctx)
+ .service(service)
+ .path(host0ObjectPath);
+
+ auto res = co_await client.current_host_state();
+
+ if (res != stateOn && res != stateOff)
+ {
+ error("Unexpected power state: {STATE}", "STATE", res);
+ }
+
+ co_return res;
+}
+
+} // namespace phosphor::software::host_power