system: Report LED state via sysrepo
In the previous commits czechlight-system YANG model was extended with
container for LED status reporting. This commit introduces
implementation of the model.
Currently we use pull ops data subscription. Maybe we can refactor
this to push in the future?
Change-Id: Ie2d257737f144085306d6c157d14ac500b3dee71
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 24ecff8..c0ec1a1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -213,6 +213,8 @@
src/system/Rtnetlink.h
src/system/IETFInterfaces.cpp
src/system/IETFInterfaces.h
+ src/system/LED.cpp
+ src/system/LED.h
)
target_link_libraries(velia-system
PUBLIC
@@ -410,6 +412,13 @@
RESOURCE_LOCK sysrepo
)
+ velia_test(sysrepo_system-leds velia-system)
+ set_tests_properties(
+ test-sysrepo_system-leds
+ PROPERTIES FIXTURES_REQUIRED sysrepo:env:sysrepo-czechlight-system
+ RESOURCE_LOCK sysrepo
+ )
+
velia_test(sysrepo_system-network velia-system)
target_link_libraries(test-sysrepo_system-network FsTestUtils)
set_tests_properties(
diff --git a/src/system/LED.cpp b/src/system/LED.cpp
new file mode 100644
index 0000000..b6465d0
--- /dev/null
+++ b/src/system/LED.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 CESNET, https://photonics.cesnet.cz/
+ *
+ * Written by Tomáš Pecka <tomas.pecka@cesnet.cz>
+ *
+ */
+
+#include "LED.h"
+
+#include <utility>
+#include "utils/io.h"
+#include "utils/log.h"
+#include "utils/sysrepo.h"
+
+using namespace std::literals;
+
+namespace {
+
+const auto CZECHLIGHT_SYSTEM_MODULE_NAME = "czechlight-system"s;
+const auto CZECHLIGHT_SYSTEM_LEDS_MODULE_PREFIX = "/"s + CZECHLIGHT_SYSTEM_MODULE_NAME + ":leds/"s;
+
+}
+
+namespace velia::system {
+
+LED::LED(const std::shared_ptr<::sysrepo::Connection>& srConn, std::filesystem::path sysfsLeds)
+ : m_log(spdlog::get("system"))
+ , m_sysfsLeds(std::move(sysfsLeds))
+ , m_srSession(std::make_shared<::sysrepo::Session>(srConn))
+ , m_srSubscribe(std::make_shared<::sysrepo::Subscribe>(m_srSession))
+{
+ utils::ensureModuleImplemented(m_srSession, CZECHLIGHT_SYSTEM_MODULE_NAME, "2021-01-13");
+
+ m_srSubscribe->oper_get_items_subscribe(
+ CZECHLIGHT_SYSTEM_MODULE_NAME.c_str(),
+ [this](auto session, auto, auto, auto, auto, auto& parent) {
+ std::map<std::string, std::string> data;
+
+ for (const auto& entry : std::filesystem::directory_iterator(m_sysfsLeds)) {
+ if (!std::filesystem::is_directory(entry.path())) {
+ continue;
+ }
+
+ const std::string deviceName = entry.path().filename();
+
+ try {
+ /* actually just uint32_t is needed for the next two variables; but there is no harm in reading them as int64_t and downcasting them later (especially when the code for reading int64_t already exists)
+ * See https://github.com/torvalds/linux/commit/af0bfab907a011e146304d20d81dddce4e4d62d0
+ */
+ const auto brightness = velia::utils::readFileInt64(entry.path() / "brightness");
+ const auto max_brightness = velia::utils::readFileInt64(entry.path() / "max_brightness");
+ auto percent = brightness * 100 / max_brightness;
+ m_log->trace("Found LED '{}' with brightness of {} % (brightness {} out of {})", deviceName, percent, brightness, max_brightness);
+
+ data[CZECHLIGHT_SYSTEM_LEDS_MODULE_PREFIX + "led[name='" + deviceName + "']/brightness"] = std::to_string(percent);
+ } catch (const std::invalid_argument& e) {
+ m_log->warn("Failed reading state of the LED '{}': {}", deviceName, e.what());
+ }
+ }
+
+ utils::valuesToYang(data, {}, session, parent);
+ return SR_ERR_OK;
+ },
+ (CZECHLIGHT_SYSTEM_LEDS_MODULE_PREFIX + "*").c_str(),
+ SR_SUBSCR_PASSIVE | SR_SUBSCR_OPER_MERGE | SR_SUBSCR_CTX_REUSE);
+}
+
+}
diff --git a/src/system/LED.h b/src/system/LED.h
new file mode 100644
index 0000000..5121d1d
--- /dev/null
+++ b/src/system/LED.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2021 CESNET, https://photonics.cesnet.cz/
+ *
+ * Written by Tomáš Pecka <tomas.pecka@cesnet.cz>
+ *
+ */
+#pragma once
+
+#include <filesystem>
+#include <sysrepo-cpp/Session.hpp>
+#include "utils/log-fwd.h"
+
+namespace velia::system {
+
+class LED {
+public:
+ LED(const std::shared_ptr<::sysrepo::Connection>& srConn, std::filesystem::path sysfsLeds);
+
+private:
+ velia::Log m_log;
+ std::filesystem::path m_sysfsLeds;
+ std::shared_ptr<::sysrepo::Session> m_srSession;
+ std::shared_ptr<::sysrepo::Subscribe> m_srSubscribe;
+};
+}
diff --git a/tests/sysfs/leds/uid:blue/brightness b/tests/sysfs/leds/uid:blue/brightness
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/sysfs/leds/uid:blue/brightness
@@ -0,0 +1 @@
+0
diff --git a/tests/sysfs/leds/uid:blue/max_brightness b/tests/sysfs/leds/uid:blue/max_brightness
new file mode 100644
index 0000000..9183bf0
--- /dev/null
+++ b/tests/sysfs/leds/uid:blue/max_brightness
@@ -0,0 +1 @@
+256
diff --git a/tests/sysfs/leds/uid:green/brightness b/tests/sysfs/leds/uid:green/brightness
new file mode 100644
index 0000000..29d6383
--- /dev/null
+++ b/tests/sysfs/leds/uid:green/brightness
@@ -0,0 +1 @@
+100
diff --git a/tests/sysfs/leds/uid:green/max_brightness b/tests/sysfs/leds/uid:green/max_brightness
new file mode 100644
index 0000000..9183bf0
--- /dev/null
+++ b/tests/sysfs/leds/uid:green/max_brightness
@@ -0,0 +1 @@
+256
diff --git a/tests/sysfs/leds/uid:red/brightness b/tests/sysfs/leds/uid:red/brightness
new file mode 100644
index 0000000..9183bf0
--- /dev/null
+++ b/tests/sysfs/leds/uid:red/brightness
@@ -0,0 +1 @@
+256
diff --git a/tests/sysfs/leds/uid:red/max_brightness b/tests/sysfs/leds/uid:red/max_brightness
new file mode 100644
index 0000000..9183bf0
--- /dev/null
+++ b/tests/sysfs/leds/uid:red/max_brightness
@@ -0,0 +1 @@
+256
diff --git a/tests/sysrepo_system-leds.cpp b/tests/sysrepo_system-leds.cpp
new file mode 100644
index 0000000..5248faa
--- /dev/null
+++ b/tests/sysrepo_system-leds.cpp
@@ -0,0 +1,38 @@
+#include "trompeloeil_doctest.h"
+#include "dbus-helpers/dbus_rauc_server.h"
+#include "pretty_printers.h"
+#include "system/LED.h"
+#include "test_log_setup.h"
+#include "test_sysrepo_helpers.h"
+#include "tests/configure.cmake.h"
+
+using namespace std::literals;
+
+TEST_CASE("Sysrepo reports system LEDs")
+{
+ trompeloeil::sequence seq1;
+
+ TEST_SYSREPO_INIT_LOGS;
+ TEST_SYSREPO_INIT;
+ TEST_SYSREPO_INIT_CLIENT;
+
+ auto fakeSysfsDir = std::filesystem::path {CMAKE_CURRENT_SOURCE_DIR + "/tests/sysfs/leds/"s};
+ velia::system::LED led(srConn, fakeSysfsDir);
+
+ std::this_thread::sleep_for(10ms);
+
+ REQUIRE(dataFromSysrepo(client, "/czechlight-system:leds", SR_DS_OPERATIONAL) == std::map<std::string, std::string> {
+ {"/led[name='line:green']", ""},
+ {"/led[name='line:green']/brightness", "100"},
+ {"/led[name='line:green']/name", "line:green"},
+ {"/led[name='uid:blue']", ""},
+ {"/led[name='uid:blue']/brightness", "0"},
+ {"/led[name='uid:blue']/name", "uid:blue"},
+ {"/led[name='uid:green']", ""},
+ {"/led[name='uid:green']/brightness", "39"},
+ {"/led[name='uid:green']/name", "uid:green"},
+ {"/led[name='uid:red']", ""},
+ {"/led[name='uid:red']/brightness", "100"},
+ {"/led[name='uid:red']/name", "uid:red"},
+ });
+}