hardware: set oper-state to disabled if PSU is not present
Our PSU can be plugged or unplugged when the system is running. When
this happens whole PSU subtree is not returned to sysrepo from
ietf-hardware.
This patch changes that by returning the PSU component tree but with no
sensors and oper-state [1] of the PSU component is set to disabled.
This return value applies for situations when (a) the PSU was
unplugged before trying to read its sensors, or (b) the PSU was
unplugged during the reading.
We did not discover any other possibility of how to report a
"not-present" except for setting the oper-state leaf.
(Originally I thought that a clever rewrite of ietf-hardware
abstraction that somehow polls the current "state" of the component
would be a good idea. But the code reporting PSU component works
differently than the rest of the sensors and we only want to support
disabled/enabled for this component. Therefore I only simply edited the
PSU code.)
[1] https://www.rfc-editor.org/rfc/rfc8348
Change-Id: I6a8a992105d42df7491b1c10614ead680836de65
diff --git a/src/ietf-hardware/FspYhPsu.cpp b/src/ietf-hardware/FspYhPsu.cpp
index 3d25139..f41aca5 100644
--- a/src/ietf-hardware/FspYhPsu.cpp
+++ b/src/ietf-hardware/FspYhPsu.cpp
@@ -72,7 +72,8 @@
FspYhPsu::FspYhPsu(const std::filesystem::path& hwmonDir, const std::string& psuName, std::shared_ptr<TransientI2C> i2c)
: m_i2c(i2c)
, m_hwmonDir(hwmonDir)
- , m_psuName(psuName)
+ , m_namePrefix("ne:"s + psuName)
+ , m_staticData(velia::ietf_hardware::data_reader::StaticData(m_namePrefix, "ne", {{"class", "iana-hardware:power-supply"}})())
{
m_exit = false;
m_psuWatcher = std::thread([this] {
@@ -120,30 +121,33 @@
void FspYhPsu::createPower()
{
m_hwmon = std::make_shared<velia::ietf_hardware::sysfs::HWMon>(m_hwmonDir);
- const auto prefix = "ne:"s + m_psuName;
- using velia::ietf_hardware::data_reader::StaticData;
using velia::ietf_hardware::data_reader::SysfsValue;
using velia::ietf_hardware::data_reader::Fans;
using velia::ietf_hardware::data_reader::SensorType;
- m_properties.emplace_back(StaticData(prefix, "ne", {{"class", "iana-hardware:power-supply"}}));
- m_properties.emplace_back(SysfsValue<SensorType::Temperature>(prefix + ":temperature-1", prefix, m_hwmon, 1));
- m_properties.emplace_back(SysfsValue<SensorType::Temperature>(prefix + ":temperature-2", prefix, m_hwmon, 2));
- m_properties.emplace_back(SysfsValue<SensorType::Current>(prefix + ":current-in", prefix, m_hwmon, 1));
- m_properties.emplace_back(SysfsValue<SensorType::Current>(prefix + ":current-12V", prefix, m_hwmon, 2));
- m_properties.emplace_back(SysfsValue<SensorType::VoltageAC>(prefix + ":voltage-in", prefix, m_hwmon, 1));
- m_properties.emplace_back(SysfsValue<SensorType::VoltageDC>(prefix + ":voltage-12V", prefix, m_hwmon, 2));
- m_properties.emplace_back(SysfsValue<SensorType::Power>(prefix + ":power-in", prefix, m_hwmon, 1));
- m_properties.emplace_back(SysfsValue<SensorType::Power>(prefix + ":power-out", prefix, m_hwmon, 2));
- m_properties.emplace_back(Fans(prefix + ":fan", prefix, m_hwmon, 1));
- m_properties.emplace_back(SysfsValue<SensorType::Current>(prefix + ":current-5Vsb", prefix, m_hwmon, 3));
- m_properties.emplace_back(SysfsValue<SensorType::VoltageDC>(prefix + ":voltage-5Vsb", prefix, m_hwmon, 3));
+ m_properties.emplace_back(SysfsValue<SensorType::Temperature>(m_namePrefix + ":temperature-1", m_namePrefix, m_hwmon, 1));
+ m_properties.emplace_back(SysfsValue<SensorType::Temperature>(m_namePrefix + ":temperature-2", m_namePrefix, m_hwmon, 2));
+ m_properties.emplace_back(SysfsValue<SensorType::Current>(m_namePrefix + ":current-in", m_namePrefix, m_hwmon, 1));
+ m_properties.emplace_back(SysfsValue<SensorType::Current>(m_namePrefix + ":current-12V", m_namePrefix, m_hwmon, 2));
+ m_properties.emplace_back(SysfsValue<SensorType::VoltageAC>(m_namePrefix + ":voltage-in", m_namePrefix, m_hwmon, 1));
+ m_properties.emplace_back(SysfsValue<SensorType::VoltageDC>(m_namePrefix + ":voltage-12V", m_namePrefix, m_hwmon, 2));
+ m_properties.emplace_back(SysfsValue<SensorType::Power>(m_namePrefix + ":power-in", m_namePrefix, m_hwmon, 1));
+ m_properties.emplace_back(SysfsValue<SensorType::Power>(m_namePrefix + ":power-out", m_namePrefix, m_hwmon, 2));
+ m_properties.emplace_back(Fans(m_namePrefix + ":fan", m_namePrefix, m_hwmon, 1));
+ m_properties.emplace_back(SysfsValue<SensorType::Current>(m_namePrefix + ":current-5Vsb", m_namePrefix, m_hwmon, 3));
+ m_properties.emplace_back(SysfsValue<SensorType::VoltageDC>(m_namePrefix + ":voltage-5Vsb", m_namePrefix, m_hwmon, 3));
}
DataTree FspYhPsu::readValues()
{
- std::map<std::string, std::string> res;
-
std::unique_lock lock(m_mtx);
+
+ DataTree res(m_staticData);
+
+ if (m_properties.empty()) {
+ res["/ietf-hardware:hardware/component[name='" + m_namePrefix + "']/state/oper-state"] = "disabled";
+ return res;
+ }
+
for (auto& reader : m_properties) {
try {
res.merge(reader());
@@ -152,9 +156,14 @@
// read can fail. We must react to this and catch the exception from readFileInt64. If we cannot get all
// data, we'll consider the data we got as invalid, so we'll return an empty map.
spdlog::get("hardware")->warn("Couldn't read PSU sysfs data (maybe the PSU was just ejected?): "s + ex.what());
+
+ res = m_staticData;
+ res["/ietf-hardware:hardware/component[name='" + m_namePrefix + "']/state/oper-state"] = "disabled";
+
lock.unlock();
m_cond.notify_all();
- return {};
+
+ return res;
}
}
diff --git a/src/ietf-hardware/FspYhPsu.h b/src/ietf-hardware/FspYhPsu.h
index f4c708f..27b9952 100644
--- a/src/ietf-hardware/FspYhPsu.h
+++ b/src/ietf-hardware/FspYhPsu.h
@@ -44,6 +44,9 @@
std::string m_psuName;
std::shared_ptr<velia::ietf_hardware::sysfs::HWMon> m_hwmon;
+
+ std::string m_namePrefix;
+ velia::ietf_hardware::DataTree m_staticData;
std::vector<std::function<velia::ietf_hardware::DataTree()>> m_properties;
void createPower();
diff --git a/tests/hardware_fspyhpsu.cpp b/tests/hardware_fspyhpsu.cpp
index 01b319e..c7139f9 100644
--- a/tests/hardware_fspyhpsu.cpp
+++ b/tests/hardware_fspyhpsu.cpp
@@ -2,6 +2,7 @@
#include <fstream>
#include "fs-helpers/utils.h"
#include "ietf-hardware/FspYhPsu.h"
+#include "ietf-hardware/IETFHardware.h"
#include "pretty_printers.h"
#include "test_log_setup.h"
#include "test_sysrepo_helpers.h"
@@ -85,12 +86,19 @@
psu = std::make_shared<velia::ietf_hardware::FspYhPsu>(fakeHwmonRoot, "psu", fakeI2c);
+ const velia::ietf_hardware::DataTree expectedDisabled = {
+ {"/ietf-hardware:hardware/component[name='ne:psu']/class", "iana-hardware:power-supply"},
+ {"/ietf-hardware:hardware/component[name='ne:psu']/parent", "ne"},
+ {"/ietf-hardware:hardware/component[name='ne:psu']/state/oper-state", "disabled"}
+ };
+
for (auto i : {0, 1, 2, 3, 4}) {
std::this_thread::sleep_for(std::chrono::seconds(4));
velia::ietf_hardware::DataTree expected;
switch (i) {
case 0:
+ expected = expectedDisabled;
break;
case 1:
expected = {
@@ -194,13 +202,16 @@
};
break;
case 2:
+ expected = expectedDisabled;
break;
case 3:
// Here I simulate read failure by a file from the hwmon directory. This happens when the user wants data from
// a PSU that's no longer there and the watcher thread didn't unbind it yet.
fakeI2c->removeHwmonFile("temp1_input");
+ expected = expectedDisabled;
break;
case 4:
+ expected = expectedDisabled;
break;
}