Make specific UNA sensors not trigger failsafe

By convention, sensors at some states like 'not present',
'power state not matching' are marked as 'unavailable' on dbus.
At such states, some specific sensors should not be considered as
failed and trigger pid 'failsafe'.

A typical example is when a system is powered-off, its CPU/DIMM temp
sensors are 'unavailable', these sensors should not be treated as
'failed' and trigger pid 'failsafe'. This is necessary for systems
whose Fans will keep working when the CPU is off.

This feature is configurable per sensor (valid on thermal sensors). It
can be enabled by setting the Pid controller option
"InputUnavailableAsFailed" to 'false' when one configuring the PID module
via entity-manager, or by setting the sensor option "unavailableAsFailed"
to 'false' when one configuring the PID module via JSON. (These options are
optional and default to 'true')

Tested:
1. On a Fan 'always-on' system, enabale this feature on CPU temp sensors,
poweroff the system, 'unavailable' CPU temp sensors do not trigger the
failsafe mode.
2. 'Unavailable' Fans still trigger the failsafe mode.
3. 'Unfunctional' or 'failed' sensors still trigger the failsafe mode.

Signed-off-by: Zheng Song <zheng.song@intel.com>
Change-Id: I1dd1d76466f43e7dcf51c161c96714f1bcfae88d
diff --git a/test/dbus_passive_unittest.cpp b/test/dbus_passive_unittest.cpp
index e6f07a0..ce1f017 100644
--- a/test/dbus_passive_unittest.cpp
+++ b/test/dbus_passive_unittest.cpp
@@ -84,11 +84,13 @@
                     prop->unit = "x";
                     prop->min = 0;
                     prop->max = 0;
+                    prop->available = true;
                 }));
         EXPECT_CALL(*helper, thresholdsAsserted(StrEq("asdf"), StrEq(path)))
             .WillOnce(Return(false));
 
         auto info = conf::SensorConfig();
+        info.unavailableAsFailed = true;
         ri = DbusPassive::createDbusPassive(bus_mock, type, id,
                                             std::move(helper), &info, nullptr);
         passive = reinterpret_cast<DbusPassive*>(ri.get());
@@ -287,6 +289,7 @@
     ReadReturn r = passive->read();
     EXPECT_EQ(0.01, r.value);
 }
+
 TEST_F(DbusPassiveTestObj, VerifyCriticalThresholdAssert)
 {
 
@@ -436,6 +439,359 @@
     EXPECT_EQ(failed, false);
 }
 
+TEST_F(DbusPassiveTestObj, VerifyAvailableDeassert)
+{
+
+    // Verifies when Availble is deasserted && unavailableAsFailed == true,
+    // the sensor goes into error state
+    EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
+        .WillOnce(Return(nullptr));
+    sdbusplus::message::message msg(nullptr, &sdbus_mock);
+
+    const char* property = "Available";
+    bool asserted = false;
+    const char* intf = "xyz.openbmc_project.State.Decorator.Availability";
+
+    passive->setAvailable(true);
+
+    EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
+        .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
+            const char** s = static_cast<const char**>(p);
+            // Read the first parameter, the string.
+            *s = intf;
+            return 0;
+        }))
+        .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
+            const char** s = static_cast<const char**>(p);
+            *s = property;
+            // Read the string in the pair (dictionary).
+            return 0;
+        }));
+
+    // std::map
+    EXPECT_CALL(sdbus_mock,
+                sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
+        .WillOnce(Return(0));
+
+    // while !at_end()
+    EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
+        .WillOnce(Return(0))
+        .WillOnce(Return(1)); // So it exits the loop after reading one pair.
+
+    // std::pair
+    EXPECT_CALL(sdbus_mock,
+                sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
+        .WillOnce(Return(0));
+
+    EXPECT_CALL(sdbus_mock,
+                sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
+        .WillOnce(Return(0));
+    EXPECT_CALL(sdbus_mock,
+                sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
+        .WillOnce(Return(0));
+    EXPECT_CALL(sdbus_mock,
+                sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
+        .WillOnce(Return(1));
+    EXPECT_CALL(sdbus_mock,
+                sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
+        .WillOnce(Return(0));
+
+    EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
+        .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
+            bool* s = static_cast<bool*>(p);
+            *s = asserted;
+            return 0;
+        }));
+
+    EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
+        .WillOnce(Return(0))  /* variant. */
+        .WillOnce(Return(0))  /* std::pair */
+        .WillOnce(Return(0)); /* std::map */
+
+    int rv = handleSensorValue(msg, passive);
+    EXPECT_EQ(rv, 0); // It's always 0.
+    bool failed = passive->getFailed();
+    EXPECT_EQ(failed, true);
+}
+
+TEST_F(DbusPassiveTestObj, VerifyAvailableAssert)
+{
+
+    // Verifies when Availble is asserted && unavailableAsFailed == true,
+    // an error sensor goes back to normal state
+    EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
+        .WillOnce(Return(nullptr));
+    sdbusplus::message::message msg(nullptr, &sdbus_mock);
+
+    const char* property = "Available";
+    bool asserted = true;
+    const char* intf = "xyz.openbmc_project.State.Decorator.Availability";
+
+    passive->setAvailable(false);
+    bool failed = passive->getFailed();
+    EXPECT_EQ(failed, true);
+
+    EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
+        .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
+            const char** s = static_cast<const char**>(p);
+            // Read the first parameter, the string.
+            *s = intf;
+            return 0;
+        }))
+        .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
+            const char** s = static_cast<const char**>(p);
+            *s = property;
+            // Read the string in the pair (dictionary).
+            return 0;
+        }));
+
+    // std::map
+    EXPECT_CALL(sdbus_mock,
+                sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
+        .WillOnce(Return(0));
+
+    // while !at_end()
+    EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
+        .WillOnce(Return(0))
+        .WillOnce(Return(1)); // So it exits the loop after reading one pair.
+
+    // std::pair
+    EXPECT_CALL(sdbus_mock,
+                sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
+        .WillOnce(Return(0));
+
+    EXPECT_CALL(sdbus_mock,
+                sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
+        .WillOnce(Return(0));
+    EXPECT_CALL(sdbus_mock,
+                sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
+        .WillOnce(Return(0));
+    EXPECT_CALL(sdbus_mock,
+                sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
+        .WillOnce(Return(1));
+    EXPECT_CALL(sdbus_mock,
+                sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
+        .WillOnce(Return(0));
+
+    EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
+        .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
+            bool* s = static_cast<bool*>(p);
+            *s = asserted;
+            return 0;
+        }));
+
+    EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
+        .WillOnce(Return(0))  /* variant. */
+        .WillOnce(Return(0))  /* std::pair */
+        .WillOnce(Return(0)); /* std::map */
+
+    int rv = handleSensorValue(msg, passive);
+    EXPECT_EQ(rv, 0); // It's always 0.
+    failed = passive->getFailed();
+    EXPECT_EQ(failed, false);
+}
+
+class DbusPassiveTestUnaSensorNotAsFailedObj : public ::testing::Test
+{
+  protected:
+    DbusPassiveTestUnaSensorNotAsFailedObj() :
+        sdbus_mock(),
+        bus_mock(std::move(sdbusplus::get_mocked_new(&sdbus_mock))),
+        helper(std::make_unique<DbusHelperMock>())
+    {
+        EXPECT_CALL(*helper, getService(StrEq(SensorIntf), StrEq(path)))
+            .WillOnce(Return("asdf"));
+
+        EXPECT_CALL(*helper,
+                    getProperties(StrEq("asdf"), StrEq(path), NotNull()))
+            .WillOnce(
+                Invoke([&](const std::string& service, const std::string& path,
+                           SensorProperties* prop) {
+                    prop->scale = _scale;
+                    prop->value = _value;
+                    prop->unit = "x";
+                    prop->min = 0;
+                    prop->max = 0;
+                    prop->available = true;
+                }));
+        EXPECT_CALL(*helper, thresholdsAsserted(StrEq("asdf"), StrEq(path)))
+            .WillOnce(Return(false));
+
+        auto info = conf::SensorConfig();
+        info.unavailableAsFailed = false;
+        ri = DbusPassive::createDbusPassive(bus_mock, type, id,
+                                            std::move(helper), &info, nullptr);
+        passive = reinterpret_cast<DbusPassive*>(ri.get());
+        EXPECT_FALSE(passive == nullptr);
+    }
+
+    sdbusplus::SdBusMock sdbus_mock;
+    sdbusplus::bus::bus bus_mock;
+    std::unique_ptr<DbusHelperMock> helper;
+    std::string type = "temp";
+    std::string id = "id";
+    std::string path = "/xyz/openbmc_project/sensors/temperature/id";
+    int64_t _scale = -3;
+    int64_t _value = 10;
+
+    std::unique_ptr<ReadInterface> ri;
+    DbusPassive* passive;
+};
+
+TEST_F(DbusPassiveTestUnaSensorNotAsFailedObj, VerifyAvailableDeassert)
+{
+
+    // Verifies when Availble is deasserted && unavailableAsFailed == false,
+    // the sensor remains at OK state but reading goes to NaN.
+    EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
+        .WillOnce(Return(nullptr));
+    sdbusplus::message::message msg(nullptr, &sdbus_mock);
+
+    const char* property = "Available";
+    bool asserted = false;
+    const char* intf = "xyz.openbmc_project.State.Decorator.Availability";
+
+    passive->setAvailable(true);
+
+    EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
+        .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
+            const char** s = static_cast<const char**>(p);
+            // Read the first parameter, the string.
+            *s = intf;
+            return 0;
+        }))
+        .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
+            const char** s = static_cast<const char**>(p);
+            *s = property;
+            // Read the string in the pair (dictionary).
+            return 0;
+        }));
+
+    // std::map
+    EXPECT_CALL(sdbus_mock,
+                sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
+        .WillOnce(Return(0));
+
+    // while !at_end()
+    EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
+        .WillOnce(Return(0))
+        .WillOnce(Return(1)); // So it exits the loop after reading one pair.
+
+    // std::pair
+    EXPECT_CALL(sdbus_mock,
+                sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
+        .WillOnce(Return(0));
+
+    EXPECT_CALL(sdbus_mock,
+                sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
+        .WillOnce(Return(0));
+    EXPECT_CALL(sdbus_mock,
+                sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
+        .WillOnce(Return(0));
+    EXPECT_CALL(sdbus_mock,
+                sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
+        .WillOnce(Return(1));
+    EXPECT_CALL(sdbus_mock,
+                sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
+        .WillOnce(Return(0));
+
+    EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
+        .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
+            bool* s = static_cast<bool*>(p);
+            *s = asserted;
+            return 0;
+        }));
+
+    EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
+        .WillOnce(Return(0))  /* variant. */
+        .WillOnce(Return(0))  /* std::pair */
+        .WillOnce(Return(0)); /* std::map */
+
+    int rv = handleSensorValue(msg, passive);
+    EXPECT_EQ(rv, 0); // It's always 0.
+    bool failed = passive->getFailed();
+    EXPECT_EQ(failed, false);
+    ReadReturn r = passive->read();
+    EXPECT_FALSE(std::isfinite(r.value));
+}
+
+TEST_F(DbusPassiveTestUnaSensorNotAsFailedObj, VerifyAvailableAssert)
+{
+
+    // Verifies when a sensor's state goes from unavailble to available
+    // && unavailableAsFailed == false, this sensor remains at OK state.
+    EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
+        .WillOnce(Return(nullptr));
+    sdbusplus::message::message msg(nullptr, &sdbus_mock);
+
+    const char* property = "Available";
+    bool asserted = true;
+    const char* intf = "xyz.openbmc_project.State.Decorator.Availability";
+
+    passive->setAvailable(false);
+    bool failed = passive->getFailed();
+    EXPECT_EQ(failed, false);
+
+    EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
+        .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
+            const char** s = static_cast<const char**>(p);
+            // Read the first parameter, the string.
+            *s = intf;
+            return 0;
+        }))
+        .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
+            const char** s = static_cast<const char**>(p);
+            *s = property;
+            // Read the string in the pair (dictionary).
+            return 0;
+        }));
+
+    // std::map
+    EXPECT_CALL(sdbus_mock,
+                sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
+        .WillOnce(Return(0));
+
+    // while !at_end()
+    EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
+        .WillOnce(Return(0))
+        .WillOnce(Return(1)); // So it exits the loop after reading one pair.
+
+    // std::pair
+    EXPECT_CALL(sdbus_mock,
+                sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
+        .WillOnce(Return(0));
+
+    EXPECT_CALL(sdbus_mock,
+                sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
+        .WillOnce(Return(0));
+    EXPECT_CALL(sdbus_mock,
+                sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
+        .WillOnce(Return(0));
+    EXPECT_CALL(sdbus_mock,
+                sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
+        .WillOnce(Return(1));
+    EXPECT_CALL(sdbus_mock,
+                sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
+        .WillOnce(Return(0));
+
+    EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
+        .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
+            bool* s = static_cast<bool*>(p);
+            *s = asserted;
+            return 0;
+        }));
+
+    EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
+        .WillOnce(Return(0))  /* variant. */
+        .WillOnce(Return(0))  /* std::pair */
+        .WillOnce(Return(0)); /* std::map */
+
+    int rv = handleSensorValue(msg, passive);
+    EXPECT_EQ(rv, 0); // It's always 0.
+    failed = passive->getFailed();
+    EXPECT_EQ(failed, false);
+}
+
 void GetPropertiesMax3k(const std::string& service, const std::string& path,
                         SensorProperties* prop)
 {