Introduce ignoreFailIfHostOff config setting
Some sensors only provide valid readings when the host is powered on.
This change introduces the `ignoreFailIfHostOff` configuration option to
differentiate between unavailable readings based on host state.
- Host OFF: Sensor unavailable is acceptable and will not be treated as
a failure.
- Host ON (running): Sensor unavailable is unexpected, and the failsafe
mode will be triggered.
This ensures that sensors dependent on host power state are handled
correctly without causing unnecessary failsafe triggers during host-off.
Tested on Catalina: failsafe mode is not triggered when the host is off.
- config.json example: add `ignoreFailIfHostOff` for sensors only
available when the host is on.
```
{
"sensors": [
......
{
"name": "HDDBOARD_SSD0_TEMP_C",
"type": "temp",
"readPath": "/xyz/openbmc_project/sensors/temperature/HDDBOARD_SSD0_TEMP_C",
"timeout": 0,
"ignoreFailIfHostOff": true,
"ignoreDbusMinMax": true
},
{
"name": "HDDBOARD_SSD2_TEMP_C",
"type": "temp",
"readPath": "/xyz/openbmc_project/sensors/temperature/HDDBOARD_SSD2_TEMP_C",
"timeout": 0,
"ignoreFailIfHostOff": true,
"ignoreDbusMinMax": true
},
{
"name": "PDB_P48V_HSC1_TEMP_C",
"type": "temp",
"readPath": "/xyz/openbmc_project/sensors/temperature/PDB_P48V_HSC1_TEMP_C",
"timeout": 0,
"ignoreDbusMinMax": true
},
{
"name": "PDB_VR_P12V_N1_TEMP_C",
"type": "temp",
"readPath": "/xyz/openbmc_project/sensors/temperature/PDB_VR_P12V_N1_TEMP_C",
"timeout": 0,
"ignoreFailIfHostOff": true,
"ignoreDbusMinMax": true
},
{
"name": "PDB_VR_P12V_N2_TEMP_C",
"type": "temp",
"readPath": "/xyz/openbmc_project/sensors/temperature/PDB_VR_P12V_N2_TEMP_C",
"timeout": 0,
"ignoreFailIfHostOff": true,
"ignoreDbusMinMax": true
},
{
"name": "IOB0_NIC0_OSFP_TEMP_C",
"type": "temp",
"readPath": "/xyz/openbmc_project/sensors/temperature/IOB0_NIC0_OSFP_TEMP_C",
"timeout": 0,
"ignoreFailIfHostOff": true,
"ignoreDbusMinMax": true
},
{
"name": "IOB0_NIC1_OSFP_TEMP_C",
"type": "temp",
"readPath": "/xyz/openbmc_project/sensors/temperature/IOB0_NIC1_OSFP_TEMP_C",
"timeout": 0,
"ignoreFailIfHostOff": true,
"ignoreDbusMinMax": true
},
{
"name": "IOB1_NIC0_OSFP_TEMP_C",
"type": "temp",
"readPath": "/xyz/openbmc_project/sensors/temperature/IOB1_NIC0_OSFP_TEMP_C",
"timeout": 0,
"ignoreFailIfHostOff": true,
"ignoreDbusMinMax": true
},
{
"name": "IOB1_NIC1_OSFP_TEMP_C",
"type": "temp",
"readPath": "/xyz/openbmc_project/sensors/temperature/IOB1_NIC1_OSFP_TEMP_C",
"timeout": 0,
"ignoreFailIfHostOff": true,
"ignoreDbusMinMax": true
},
......
],
"zones": [
......
]
}
```
Change-Id: I5355c453ca3c6d918c197dcd2cc9119e471d615d
Signed-off-by: Potin Lai <potin.lai@quantatw.com>
diff --git a/hoststatemonitor.hpp b/hoststatemonitor.hpp
new file mode 100644
index 0000000..4d47705
--- /dev/null
+++ b/hoststatemonitor.hpp
@@ -0,0 +1,141 @@
+#pragma once
+
+#include <boost/container/flat_map.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/bus/match.hpp>
+#include <sdbusplus/message.hpp>
+
+#include <functional>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <variant>
+
+constexpr const char* PROPERTIES_INTERFACE = "org.freedesktop.DBus.Properties";
+constexpr const char* HOST_STATE_BUSNAME = "xyz.openbmc_project.State.Host0";
+constexpr const char* HOST_STATE_INTERFACE = "xyz.openbmc_project.State.Host";
+constexpr const char* HOST_STATE_PATH = "/xyz/openbmc_project/state/host0";
+constexpr const char* CURRENT_HOST_STATE_PROPERTY = "CurrentHostState";
+
+class HostStateMonitor
+{
+ public:
+ static HostStateMonitor& getInstance();
+ static HostStateMonitor& getInstance(sdbusplus::bus_t& bus);
+
+ ~HostStateMonitor() = default;
+
+ // Delete copy constructor and assignment operator
+ HostStateMonitor(const HostStateMonitor&) = delete;
+ HostStateMonitor& operator=(const HostStateMonitor&) = delete;
+
+ // Delete move constructor and assignment operator for singleton
+ HostStateMonitor(HostStateMonitor&&) = delete;
+ HostStateMonitor& operator=(HostStateMonitor&&) = delete;
+
+ void startMonitoring();
+ void stopMonitoring();
+ bool isPowerOn() const
+ {
+ return powerStatusOn;
+ }
+
+ private:
+ explicit HostStateMonitor(sdbusplus::bus_t& bus);
+
+ void handleStateChange(sdbusplus::message_t& message);
+ void getInitialState();
+
+ sdbusplus::bus_t& bus;
+ bool powerStatusOn;
+ std::unique_ptr<sdbusplus::bus::match_t> hostStateMatch;
+};
+
+// Implementation
+inline HostStateMonitor& HostStateMonitor::getInstance()
+{
+ static sdbusplus::bus_t defaultBus = sdbusplus::bus::new_default();
+ return getInstance(defaultBus);
+}
+
+inline HostStateMonitor& HostStateMonitor::getInstance(sdbusplus::bus_t& bus)
+{
+ static HostStateMonitor instance(bus);
+ return instance;
+}
+
+inline HostStateMonitor::HostStateMonitor(sdbusplus::bus_t& bus) :
+ bus(bus), powerStatusOn(false), hostStateMatch(nullptr)
+{
+ getInitialState();
+}
+
+inline void HostStateMonitor::startMonitoring()
+{
+ if (hostStateMatch == nullptr)
+ {
+ using namespace sdbusplus::bus::match::rules;
+
+ hostStateMatch = std::make_unique<sdbusplus::bus::match_t>(
+ bus,
+ propertiesChangedNamespace(HOST_STATE_PATH, HOST_STATE_INTERFACE),
+ [this](sdbusplus::message_t& message) {
+ handleStateChange(message);
+ });
+ }
+}
+
+inline void HostStateMonitor::stopMonitoring()
+{
+ hostStateMatch.reset();
+}
+
+inline void HostStateMonitor::handleStateChange(sdbusplus::message_t& message)
+{
+ std::string objectName;
+ boost::container::flat_map<std::string, std::variant<std::string>> values;
+
+ try
+ {
+ message.read(objectName, values);
+
+ auto findState = values.find(CURRENT_HOST_STATE_PROPERTY);
+ if (findState != values.end())
+ {
+ const std::string& stateValue =
+ std::get<std::string>(findState->second);
+ bool newPowerStatus = stateValue.ends_with(".Running");
+
+ if (newPowerStatus != powerStatusOn)
+ {
+ powerStatusOn = newPowerStatus;
+ }
+ }
+ }
+ catch (const std::exception& e)
+ {
+ std::cerr << "Failed to handle host state change: " << e.what()
+ << std::endl;
+ }
+}
+
+inline void HostStateMonitor::getInitialState()
+{
+ try
+ {
+ auto method = bus.new_method_call(HOST_STATE_BUSNAME, HOST_STATE_PATH,
+ PROPERTIES_INTERFACE, "Get");
+ method.append(HOST_STATE_INTERFACE, CURRENT_HOST_STATE_PROPERTY);
+
+ auto reply = bus.call(method);
+ std::variant<std::string> currentState;
+ reply.read(currentState);
+
+ const std::string& stateValue = std::get<std::string>(currentState);
+ powerStatusOn = stateValue.ends_with(".Running");
+ }
+ catch (const std::exception& e)
+ {
+ powerStatusOn = false;
+ }
+}