Add Fan Redundancy Support
This adds fan redundancy support to passive sensors.
If there are redundancy interfaces on dbus, we'll fail
a sensor if the status is set to failed.
Tested: Set Redundancy to Failed On Dbus, saw all fans
in collection boost. Then restarted swampd, came up
and still boosted. Set redundancy OK, and they all slowed
down.
Change-Id: I8879bef1471bbc168435d6b22dd20006b9dca133
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/Makefile.am b/Makefile.am
index 241878c..3283236 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -79,6 +79,7 @@
notimpl/readonly.cpp \
notimpl/writeonly.cpp \
dbus/util.cpp \
+ dbus/dbuspassiveredundancy.cpp \
dbus/dbuspassive.cpp \
dbus/dbusactiveread.cpp \
dbus/dbuswrite.cpp \
diff --git a/dbus/dbuspassive.cpp b/dbus/dbuspassive.cpp
index a154b02..a0dbc57 100644
--- a/dbus/dbuspassive.cpp
+++ b/dbus/dbuspassive.cpp
@@ -15,6 +15,7 @@
*/
#include "dbuspassive.hpp"
+#include "dbuspassiveredundancy.hpp"
#include "util.hpp"
#include <chrono>
@@ -27,7 +28,8 @@
std::unique_ptr<ReadInterface> DbusPassive::createDbusPassive(
sdbusplus::bus::bus& bus, const std::string& type, const std::string& id,
- DbusHelperInterface* helper, const conf::SensorConfig* info)
+ DbusHelperInterface* helper, const conf::SensorConfig* info,
+ const std::shared_ptr<DbusPassiveRedundancy>& redundancy)
{
if (helper == nullptr)
{
@@ -69,15 +71,19 @@
}
return std::make_unique<DbusPassive>(bus, type, id, helper, settings,
- failed);
+ failed, path, redundancy);
}
-DbusPassive::DbusPassive(sdbusplus::bus::bus& bus, const std::string& type,
- const std::string& id, DbusHelperInterface* helper,
- const struct SensorProperties& settings, bool failed) :
+DbusPassive::DbusPassive(
+ sdbusplus::bus::bus& bus, const std::string& type, const std::string& id,
+ DbusHelperInterface* helper, const struct SensorProperties& settings,
+ bool failed, const std::string& path,
+ const std::shared_ptr<DbusPassiveRedundancy>& redundancy) :
ReadInterface(),
_bus(bus), _signal(bus, getMatch(type, id).c_str(), dbusHandleSignal, this),
- _id(id), _helper(helper), _failed(failed)
+ _id(id), _helper(helper), _failed(failed), path(path),
+ redundancy(redundancy)
+
{
_scale = settings.scale;
_value = settings.value * pow(10, _scale);
@@ -105,6 +111,14 @@
bool DbusPassive::getFailed(void) const
{
+ if (redundancy)
+ {
+ const std::set<std::string>& failures = redundancy->getFailed();
+ if (failures.find(path) != failures.end())
+ {
+ return true;
+ }
+ }
return _failed;
}
diff --git a/dbus/dbuspassive.hpp b/dbus/dbuspassive.hpp
index ebc831d..f00a2ea 100644
--- a/dbus/dbuspassive.hpp
+++ b/dbus/dbuspassive.hpp
@@ -1,6 +1,7 @@
#pragma once
#include "conf.hpp"
+#include "dbuspassiveredundancy.hpp"
#include "interfaces.hpp"
#include "util.hpp"
@@ -34,14 +35,17 @@
class DbusPassive : public ReadInterface
{
public:
- static std::unique_ptr<ReadInterface>
- createDbusPassive(sdbusplus::bus::bus& bus, const std::string& type,
- const std::string& id, DbusHelperInterface* helper,
- const conf::SensorConfig* info);
+ static std::unique_ptr<ReadInterface> createDbusPassive(
+ sdbusplus::bus::bus& bus, const std::string& type,
+ const std::string& id, DbusHelperInterface* helper,
+ const conf::SensorConfig* info,
+ const std::shared_ptr<DbusPassiveRedundancy>& redundancy);
DbusPassive(sdbusplus::bus::bus& bus, const std::string& type,
const std::string& id, DbusHelperInterface* helper,
- const struct SensorProperties& settings, bool failed);
+ const struct SensorProperties& settings, bool failed,
+ const std::string& path,
+ const std::shared_ptr<DbusPassiveRedundancy>& redundancy);
ReadReturn read(void) override;
bool getFailed(void) const override;
@@ -65,6 +69,9 @@
double _max = 0;
double _min = 0;
bool _failed = false;
+
+ std::string path;
+ std::shared_ptr<DbusPassiveRedundancy> redundancy;
/* The last time the value was refreshed, not necessarily changed. */
std::chrono::high_resolution_clock::time_point _updated;
};
diff --git a/dbus/dbuspassiveredundancy.cpp b/dbus/dbuspassiveredundancy.cpp
new file mode 100644
index 0000000..c7a1d0f
--- /dev/null
+++ b/dbus/dbuspassiveredundancy.cpp
@@ -0,0 +1,177 @@
+/*
+// Copyright (c) 2019 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include "dbuspassiveredundancy.hpp"
+
+#include <iostream>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/bus/match.hpp>
+#include <set>
+#include <unordered_map>
+#include <variant>
+
+namespace properties
+{
+
+constexpr const char* interface = "org.freedesktop.DBus.Properties";
+constexpr const char* get = "Get";
+constexpr const char* getAll = "GetAll";
+
+} // namespace properties
+
+namespace redundancy
+{
+
+constexpr const char* collection = "Collection";
+constexpr const char* status = "Status";
+constexpr const char* interface = "xyz.openbmc_project.Control.FanRedundancy";
+
+} // namespace redundancy
+
+DbusPassiveRedundancy::DbusPassiveRedundancy(sdbusplus::bus::bus& bus) :
+ match(bus,
+ "type='signal',member='PropertiesChanged',arg0namespace='" +
+ std::string(redundancy::interface) + "'",
+ std::move([this](sdbusplus::message::message& message) {
+ std::string objectName;
+ std::unordered_map<
+ std::string,
+ std::variant<std::string, std::vector<std::string>>>
+ result;
+ try
+ {
+ message.read(objectName, result);
+ }
+ catch (sdbusplus::exception_t&)
+ {
+ std::cerr << "Error reading match data";
+ return;
+ }
+ auto findStatus = result.find("Status");
+ if (findStatus == result.end())
+ {
+ return;
+ }
+ std::string status = std::get<std::string>(findStatus->second);
+
+ auto methodCall = passiveBus.new_method_call(
+ message.get_sender(), message.get_path(),
+ properties::interface, properties::get);
+ methodCall.append(redundancy::interface, redundancy::collection);
+ std::variant<std::vector<std::string>> collection;
+
+ try
+ {
+ auto reply = passiveBus.call(methodCall);
+ reply.read(collection);
+ }
+ catch (sdbusplus::exception_t&)
+ {
+ std::cerr << "Error reading match data";
+ return;
+ }
+
+ auto data = std::get<std::vector<std::string>>(collection);
+ if (status.rfind("Failed") != std::string::npos)
+ {
+ failed.insert(data.begin(), data.end());
+ }
+ else
+ {
+ for (const auto& d : data)
+ {
+ failed.erase(d);
+ }
+ }
+ })),
+ passiveBus(bus)
+{
+ populateFailures();
+}
+
+void DbusPassiveRedundancy::populateFailures(void)
+{
+ auto mapper = passiveBus.new_method_call(
+ "xyz.openbmc_project.ObjectMapper",
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetSubTree");
+ mapper.append("/", 0, std::array<const char*, 1>{redundancy::interface});
+ std::unordered_map<
+ std::string, std::unordered_map<std::string, std::vector<std::string>>>
+ respData;
+ try
+ {
+ auto resp = passiveBus.call(mapper);
+ resp.read(respData);
+ }
+ catch (sdbusplus::exception_t&)
+ {
+ std::cerr << "Populate Failures Mapper Error\n";
+ return;
+ }
+
+ /*
+ * The subtree response looks like:
+ * {path :
+ * {busname:
+ * {interface, interface, interface, ...}
+ * }
+ * }
+ *
+ * This loops through this structure to pre-poulate the already failed items
+ */
+
+ for (const auto& [path, interfaceDict] : respData)
+ {
+ for (const auto& [owner, _] : interfaceDict)
+ {
+ auto call = passiveBus.new_method_call(owner.c_str(), path.c_str(),
+ properties::interface,
+ properties::getAll);
+ call.append(redundancy::interface);
+
+ std::unordered_map<
+ std::string,
+ std::variant<std::string, std::vector<std::string>>>
+ getAll;
+ try
+ {
+ auto data = passiveBus.call(call);
+ data.read(getAll);
+ }
+ catch (sdbusplus::exception_t&)
+ {
+ std::cerr << "Populate Failures Mapper Error\n";
+ return;
+ }
+ std::string status =
+ std::get<std::string>(getAll[redundancy::status]);
+ if (status.rfind("Failed") == std::string::npos)
+ {
+ continue;
+ }
+ std::vector<std::string> collection =
+ std::get<std::vector<std::string>>(
+ getAll[redundancy::collection]);
+ failed.insert(collection.begin(), collection.end());
+ }
+ }
+}
+
+const std::set<std::string>& DbusPassiveRedundancy::getFailed()
+{
+ return failed;
+}
\ No newline at end of file
diff --git a/dbus/dbuspassiveredundancy.hpp b/dbus/dbuspassiveredundancy.hpp
new file mode 100644
index 0000000..3d7e2a6
--- /dev/null
+++ b/dbus/dbuspassiveredundancy.hpp
@@ -0,0 +1,43 @@
+/*
+// Copyright (c) 2019 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#pragma once
+
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/bus/match.hpp>
+#include <set>
+
+/*
+ * DbusPassiveRedundancy monitors the fan redundancy interface via dbus match
+ * for changes. When the "Status" property changes to Failed, all sensors in
+ * the interface's "Collection" property are added to the failed member. This
+ * set can then be queried by a sensor to see if they are part of a failed
+ * redundancy collection. When this happens, they get marked as failed.
+ */
+
+class DbusPassiveRedundancy
+{
+ public:
+ DbusPassiveRedundancy(sdbusplus::bus::bus& bus);
+ const std::set<std::string>& getFailed(void);
+
+ private:
+ void populateFailures(void);
+
+ sdbusplus::bus::match::match match;
+ std::set<std::string> failed;
+ sdbusplus::bus::bus& passiveBus;
+};
diff --git a/sensors/builder.cpp b/sensors/builder.cpp
index f5c01b3..4da1cf2 100644
--- a/sensors/builder.cpp
+++ b/sensors/builder.cpp
@@ -68,8 +68,23 @@
switch (rtype)
{
case IOInterfaceType::DBUSPASSIVE:
- ri = DbusPassive::createDbusPassive(
- passiveListeningBus, info->type, name, &helper, info);
+ // we only need to make one match based on the dbus object
+ static std::shared_ptr<DbusPassiveRedundancy> redundancy =
+ std::make_shared<DbusPassiveRedundancy>(
+ passiveListeningBus);
+
+ if (info->type == "fan")
+ {
+ ri = DbusPassive::createDbusPassive(
+ passiveListeningBus, info->type, name, &helper, info,
+ redundancy);
+ }
+ else
+ {
+ ri = DbusPassive::createDbusPassive(passiveListeningBus,
+ info->type, name,
+ &helper, info, nullptr);
+ }
if (ri == nullptr)
{
throw SensorBuildException(
diff --git a/test/Makefile.am b/test/Makefile.am
index fbc78bf..3d74c88 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -63,7 +63,7 @@
dbus_passive_unittest_SOURCES = dbus_passive_unittest.cpp
dbus_passive_unittest_LDADD = $(top_builddir)/dbus/util.o \
- $(top_builddir)/dbus/dbuspassive.o
+ $(top_builddir)/dbus/dbuspassive.o $(top_builddir)/dbus/dbuspassiveredundancy.o
dbus_active_unittest_SOURCES = dbus_active_unittest.cpp
dbus_active_unittest_LDADD = $(top_builddir)/dbus/dbusactiveread.o
diff --git a/test/dbus_passive_unittest.cpp b/test/dbus_passive_unittest.cpp
index 2fc3471..84557ee 100644
--- a/test/dbus_passive_unittest.cpp
+++ b/test/dbus_passive_unittest.cpp
@@ -31,8 +31,8 @@
DbusHelperMock helper;
auto info = conf::SensorConfig();
- std::unique_ptr<ReadInterface> ri =
- DbusPassive::createDbusPassive(bus_mock, type, id, &helper, &info);
+ std::unique_ptr<ReadInterface> ri = DbusPassive::createDbusPassive(
+ bus_mock, type, id, &helper, &info, nullptr);
EXPECT_EQ(ri, nullptr);
}
@@ -50,7 +50,7 @@
DbusHelperMock helper;
struct SensorProperties properties;
- DbusPassive(bus_mock, type, id, &helper, properties, false);
+ DbusPassive(bus_mock, type, id, &helper, properties, false, path, nullptr);
// Success
}
@@ -77,7 +77,8 @@
.WillOnce(Return(false));
auto info = conf::SensorConfig();
- ri = DbusPassive::createDbusPassive(bus_mock, type, id, &helper, &info);
+ ri = DbusPassive::createDbusPassive(bus_mock, type, id, &helper, &info,
+ nullptr);
passive = reinterpret_cast<DbusPassive*>(ri.get());
EXPECT_FALSE(passive == nullptr);
}