monitor: Create PowerOffRules class
This class contains a PowerOffCause and a PowerOffAction. It provides a
check() method that takes the FanHealth map which it then checks against
the cause. If the cause is satisfied, it then starts the power off
action. It provides a cancel method that will force cancel a running
action in the case that the object owner detects a system power off and
so doesn't need to run this power off anymore.
The class's configuration data is read from the JSON config file.
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I5c0c168591d6d62c894c4d036ec762797fd759af
diff --git a/monitor/json_parser.cpp b/monitor/json_parser.cpp
index 51df226..bd239c6 100644
--- a/monitor/json_parser.cpp
+++ b/monitor/json_parser.cpp
@@ -18,8 +18,12 @@
#include "conditions.hpp"
#include "json_config.hpp"
#include "nonzero_speed_trust.hpp"
+#include "power_interface.hpp"
+#include "power_off_rule.hpp"
#include "types.hpp"
+#include <fmt/format.h>
+
#include <nlohmann/json.hpp>
#include <phosphor-logging/log.hpp>
@@ -239,4 +243,152 @@
return fanDefs;
}
+PowerRuleState getPowerOffPowerRuleState(const json& powerOffConfig)
+{
+ // The state is optional and defaults to runtime
+ PowerRuleState ruleState{PowerRuleState::runtime};
+
+ if (powerOffConfig.contains("state"))
+ {
+ auto state = powerOffConfig.at("state").get<std::string>();
+ if (state == "at_pgood")
+ {
+ ruleState = PowerRuleState::atPgood;
+ }
+ else if (state != "runtime")
+ {
+ auto msg = fmt::format("Invalid power off state entry {}", state);
+ log<level::ERR>(msg.c_str());
+ throw std::runtime_error(msg.c_str());
+ }
+ }
+
+ return ruleState;
+}
+
+std::unique_ptr<PowerOffCause> getPowerOffCause(const json& powerOffConfig)
+{
+ std::unique_ptr<PowerOffCause> cause;
+
+ if (!powerOffConfig.contains("count") || !powerOffConfig.contains("cause"))
+ {
+ const auto msg =
+ "Missing 'count' or 'cause' entries in power off config";
+ log<level::ERR>(msg);
+ throw std::runtime_error(msg);
+ }
+
+ auto count = powerOffConfig.at("count").get<size_t>();
+ auto powerOffCause = powerOffConfig.at("cause").get<std::string>();
+
+ const std::map<std::string, std::function<std::unique_ptr<PowerOffCause>()>>
+ causes{
+ {"missing_fan_frus",
+ [count]() { return std::make_unique<MissingFanFRUCause>(count); }},
+ {"nonfunc_fan_rotors", [count]() {
+ return std::make_unique<NonfuncFanRotorCause>(count);
+ }}};
+
+ auto it = causes.find(powerOffCause);
+ if (it != causes.end())
+ {
+ cause = it->second();
+ }
+ else
+ {
+ auto msg =
+ fmt::format("Invalid power off cause {} in power off config JSON",
+ powerOffCause);
+ log<level::ERR>(msg.c_str());
+ throw std::runtime_error(msg.c_str());
+ }
+
+ return cause;
+}
+
+std::unique_ptr<PowerOffAction>
+ getPowerOffAction(const json& powerOffConfig,
+ std::shared_ptr<PowerInterfaceBase>& powerInterface)
+{
+ std::unique_ptr<PowerOffAction> action;
+ if (!powerOffConfig.contains("type"))
+ {
+ const auto msg = "Missing 'type' entry in power off config";
+ log<level::ERR>(msg);
+ throw std::runtime_error(msg);
+ }
+
+ auto type = powerOffConfig.at("type").get<std::string>();
+
+ if (((type == "hard") || (type == "soft")) &&
+ !powerOffConfig.contains("delay"))
+ {
+ const auto msg = "Missing 'delay' entry in power off config";
+ log<level::ERR>(msg);
+ throw std::runtime_error(msg);
+ }
+ else if ((type == "epow") &&
+ (!powerOffConfig.contains("service_mode_delay") ||
+ !powerOffConfig.contains("meltdown_delay")))
+ {
+ const auto msg = "Missing 'service_mode_delay' or 'meltdown_delay' "
+ "entry in power off config";
+ log<level::ERR>(msg);
+ throw std::runtime_error(msg);
+ }
+
+ if (type == "hard")
+ {
+ action = std::make_unique<HardPowerOff>(
+ powerOffConfig.at("delay").get<uint32_t>(), powerInterface);
+ }
+ else if (type == "soft")
+ {
+ action = std::make_unique<SoftPowerOff>(
+ powerOffConfig.at("delay").get<uint32_t>(), powerInterface);
+ }
+ else if (type == "epow")
+ {
+ action = std::make_unique<EpowPowerOff>(
+ powerOffConfig.at("service_mode_delay").get<uint32_t>(),
+ powerOffConfig.at("meltdown_delay").get<uint32_t>(),
+ powerInterface);
+ }
+ else
+ {
+ auto msg =
+ fmt::format("Invalid 'type' entry {} in power off config", type);
+ log<level::ERR>(msg.c_str());
+ throw std::runtime_error(msg.c_str());
+ }
+
+ return action;
+}
+
+std::vector<std::unique_ptr<PowerOffRule>>
+ getPowerOffRules(const json& obj,
+ std::shared_ptr<PowerInterfaceBase>& powerInterface)
+{
+ std::vector<std::unique_ptr<PowerOffRule>> rules;
+
+ if (!(obj.contains("fault_handling") &&
+ obj.at("fault_handling").contains("power_off_config")))
+ {
+ return rules;
+ }
+
+ for (const auto& config : obj.at("fault_handling").at("power_off_config"))
+ {
+ auto state = getPowerOffPowerRuleState(config);
+ auto cause = getPowerOffCause(config);
+ auto action = getPowerOffAction(config, powerInterface);
+
+ auto rule = std::make_unique<PowerOffRule>(
+ std::move(state), std::move(cause), std::move(action));
+ rules.push_back(std::move(rule));
+ }
+
+ return rules;
+}
+
} // namespace phosphor::fan::monitor