monitor: Create PowerOffAction class hierarchy
The PowerOffAction base class and its derived classes will be used to
power off a system due to fan failures.
There are 3 types of power offs:
1. HardPowerOff - Do a hard power off after a delay
2. SoftPowerOff - Do a soft power off after a delay
3. EpowPowerOff - This isn't fully defined yet, but it will involve
powering off after setting an early power off warning
somehow and then waiting through 2 delays.
The code that makes the D-Bus calls to do the power offs is in a
standalone class so that it can be be mocked in testcases.
This code also makes use of the Logger class for logging, so this commit
brings that in as a singleton.
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I83118963df4ec0b4f89619572f6935329eec3adb
diff --git a/monitor/power_off_action.hpp b/monitor/power_off_action.hpp
new file mode 100644
index 0000000..05d4ff6
--- /dev/null
+++ b/monitor/power_off_action.hpp
@@ -0,0 +1,327 @@
+#pragma once
+
+#include "logging.hpp"
+#include "power_interface.hpp"
+
+#include <fmt/format.h>
+
+#include <sdeventplus/clock.hpp>
+#include <sdeventplus/event.hpp>
+#include <sdeventplus/utility/timer.hpp>
+
+#include <chrono>
+
+namespace phosphor::fan::monitor
+{
+
+/**
+ * @class PowerOffAction
+ *
+ * This is the base class for a power off action, which is
+ * used by the PowerOffRule class to do different types of
+ * power offs based on fan failures.
+ *
+ * The power off is started with the start() method, and the
+ * derived class may or may not allow it to be stopped with
+ * the cancel() method, which is really only useful when
+ * there is a delay before the power off.
+ *
+ * It uses the PowerInterfaceBase object pointer to perform
+ * the D-Bus call to do the power off, so it can be mocked
+ * for testing.
+ */
+class PowerOffAction
+{
+ public:
+ PowerOffAction() = delete;
+ virtual ~PowerOffAction() = default;
+ PowerOffAction(const PowerOffAction&) = delete;
+ PowerOffAction& operator=(const PowerOffAction&) = delete;
+ PowerOffAction(PowerOffAction&&) = delete;
+ PowerOffAction& operator=(PowerOffAction&&) = delete;
+
+ /**
+ * @brief Constructor
+ *
+ * @param[in] name - The action name. Used for tracing.
+ * powerInterface - The object used to invoke the power off.
+ */
+ PowerOffAction(const std::string& name,
+ std::shared_ptr<PowerInterfaceBase> powerInterface) :
+ _name(name),
+ _powerIface(std::move(powerInterface)),
+ _event(sdeventplus::Event::get_default())
+ {}
+
+ /**
+ * @brief Starts the power off.
+ *
+ * Though this occurs in the child class, usually this
+ * involves starting a timer and then powering off when it
+ * times out.
+ */
+ virtual void start() = 0;
+
+ /**
+ * @brief Attempts to cancel the power off, if the derived
+ * class allows it, and assuming the power off hasn't
+ * already happened.
+ *
+ * The 'force' parameter is mainly for use when something else
+ * powered off the system so this action doesn't need to run
+ * anymore even if it isn't usually cancelable.
+ *
+ * @param[in] force - If the cancel should be forced
+ *
+ * @return bool - If the cancel was allowed/successful
+ */
+ virtual bool cancel(bool force) = 0;
+
+ /**
+ * @brief If the power off action is currently in progress, which
+ * usually means it's still in the delay time before the
+ * power off D-Bus command is executed.
+ *
+ * @return bool - If the action is active
+ */
+ bool active() const
+ {
+ return _active;
+ }
+
+ /**
+ * @brief Returns the name of the action
+ *
+ * @return const std::string& - The name
+ */
+ const std::string& name() const
+ {
+ return _name;
+ }
+
+ protected:
+ /**
+ * @brief The name of the action, which is set by the
+ * derived class.
+ */
+ const std::string _name;
+
+ /**
+ * @brief If the action is currently active or not.
+ */
+ bool _active = false;
+
+ /**
+ * @brief The object used to invoke the power off with.
+ */
+ std::shared_ptr<PowerInterfaceBase> _powerIface;
+
+ /**
+ * @brief The event loop object. Needed by timers.
+ */
+ sdeventplus::Event _event;
+};
+
+/**
+ * @class HardPowerOff
+ *
+ * This class is derived from the PowerOffAction class
+ * and will execute a hard power off after some delay.
+ */
+class HardPowerOff : public PowerOffAction
+{
+ public:
+ HardPowerOff() = delete;
+ ~HardPowerOff() = default;
+ HardPowerOff(const HardPowerOff&) = delete;
+ HardPowerOff& operator=(const HardPowerOff&) = delete;
+ HardPowerOff(HardPowerOff&&) = delete;
+ HardPowerOff& operator=(HardPowerOff&&) = delete;
+
+ /**
+ * @brief Constructor
+ *
+ * @param[in] delay - The amount of time in seconds to wait before
+ * doing the power off
+ * @param[in] powerInterface - The object to use to do the power off
+ */
+ HardPowerOff(uint32_t delay,
+ std::shared_ptr<PowerInterfaceBase> powerInterface) :
+ PowerOffAction("Hard Power Off: " + std::to_string(delay) + "s",
+ powerInterface),
+ _delay(delay),
+ _timer(_event, std::bind(std::mem_fn(&HardPowerOff::powerOff), this))
+ {}
+
+ /**
+ * @brief Starts a timer upon the expiration of which the
+ * hard power off will be done.
+ */
+ void start() override
+ {
+ _timer.restartOnce(_delay);
+ }
+
+ /**
+ * @brief Cancels the timer. This is always allowed.
+ *
+ * @param[in] force - If the cancel should be forced or not
+ * (not checked in this case)
+ * @return bool - Always returns true
+ */
+ bool cancel(bool) override
+ {
+ if (_timer.isEnabled())
+ {
+ _timer.setEnabled(false);
+ }
+
+ // Can always be canceled
+ return true;
+ }
+
+ /**
+ * @brief Performs the hard power off.
+ */
+ void powerOff()
+ {
+ getLogger().log(
+ fmt::format("Action '{}' executing hard power off", name()));
+ _powerIface->hardPowerOff();
+ }
+
+ private:
+ /**
+ * @brief The number of seconds to wait between starting the
+ * action and doing the power off.
+ */
+ std::chrono::seconds _delay;
+
+ /**
+ * @brief The Timer object used to handle the delay.
+ */
+ sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _timer;
+};
+
+/**
+ * @class SoftPowerOff
+ *
+ * This class is derived from the PowerOffAction class
+ * and will execute a soft power off after some delay.
+ */
+class SoftPowerOff : public PowerOffAction
+{
+ public:
+ SoftPowerOff() = delete;
+ ~SoftPowerOff() = default;
+ SoftPowerOff(const SoftPowerOff&) = delete;
+ SoftPowerOff& operator=(const SoftPowerOff&) = delete;
+ SoftPowerOff(SoftPowerOff&&) = delete;
+ SoftPowerOff& operator=(SoftPowerOff&&) = delete;
+
+ /**
+ * @brief Constructor
+ *
+ * @param[in] delay - The amount of time in seconds to wait before
+ * doing the power off
+ * @param[in] powerInterface - The object to use to do the power off
+ */
+ SoftPowerOff(uint32_t delay,
+ std::shared_ptr<PowerInterfaceBase> powerInterface) :
+ PowerOffAction("Soft Power Off: " + std::to_string(delay) + "s",
+ powerInterface),
+ _delay(delay),
+ _timer(_event, std::bind(std::mem_fn(&SoftPowerOff::powerOff), this))
+ {}
+
+ /**
+ * @brief Starts a timer upon the expiration of which the
+ * soft power off will be done.
+ */
+ void start() override
+ {
+ _timer.restartOnce(_delay);
+ }
+
+ /**
+ * @brief Cancels the timer. This is always allowed.
+ *
+ * @param[in] force - If the cancel should be forced or not
+ * (not checked in this case)
+ * @return bool - Always returns true
+ */
+ bool cancel(bool) override
+ {
+ if (_timer.isEnabled())
+ {
+ _timer.setEnabled(false);
+ }
+
+ // Can always be canceled
+ return true;
+ }
+
+ /**
+ * @brief Performs the soft power off.
+ */
+ void powerOff()
+ {
+ getLogger().log(
+ fmt::format("Action '{}' executing soft power off", name()));
+ _powerIface->softPowerOff();
+ }
+
+ private:
+ /**
+ * @brief The number of seconds to wait between starting the
+ * action and doing the power off.
+ */
+ std::chrono::seconds _delay;
+
+ /**
+ * @brief The Timer object used to handle the delay.
+ */
+ sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _timer;
+};
+
+/**
+ * @class EpowPowerOff
+ *
+ * Still TODO, but has a cancelable service mode delay followed
+ * by an uncancelable meltdown delay followed by a hard power off, with
+ * some sort of EPOW alert in there as well.
+ */
+class EpowPowerOff : public PowerOffAction
+{
+ public:
+ EpowPowerOff() = delete;
+ ~EpowPowerOff() = default;
+ EpowPowerOff(const EpowPowerOff&) = delete;
+ EpowPowerOff& operator=(const EpowPowerOff&) = delete;
+ EpowPowerOff(EpowPowerOff&&) = delete;
+ EpowPowerOff& operator=(EpowPowerOff&&) = delete;
+
+ EpowPowerOff(uint32_t serviceModeDelay, uint32_t meltdownDelay,
+ std::shared_ptr<PowerInterfaceBase> powerInterface) :
+ PowerOffAction("EPOW Power Off: " + std::to_string(serviceModeDelay) +
+ "s/" + std::to_string(meltdownDelay) + "s",
+ powerInterface),
+ _serviceModeDelay(serviceModeDelay), _meltdownDelay(meltdownDelay)
+ {}
+
+ void start() override
+ {
+ // TODO
+ }
+
+ bool cancel(bool) override
+ {
+ // TODO
+ return true;
+ }
+
+ private:
+ std::chrono::seconds _serviceModeDelay;
+ std::chrono::seconds _meltdownDelay;
+};
+} // namespace phosphor::fan::monitor