system: Turn on and off UID LED via netconf
Change-Id: Ic33a436f6bfb796379a019bd498b9919ce99a9d5
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c0ec1a1..38c274f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -412,7 +412,7 @@
RESOURCE_LOCK sysrepo
)
- velia_test(sysrepo_system-leds velia-system)
+ velia_test(sysrepo_system-leds velia-system FsTestUtils)
set_tests_properties(
test-sysrepo_system-leds
PROPERTIES FIXTURES_REQUIRED sysrepo:env:sysrepo-czechlight-system
diff --git a/src/system/LED.cpp b/src/system/LED.cpp
index b6465d0..870476b 100644
--- a/src/system/LED.cpp
+++ b/src/system/LED.cpp
@@ -9,6 +9,7 @@
#include <utility>
#include "utils/io.h"
+#include "utils/libyang.h"
#include "utils/log.h"
#include "utils/sysrepo.h"
@@ -19,6 +20,7 @@
const auto CZECHLIGHT_SYSTEM_MODULE_NAME = "czechlight-system"s;
const auto CZECHLIGHT_SYSTEM_LEDS_MODULE_PREFIX = "/"s + CZECHLIGHT_SYSTEM_MODULE_NAME + ":leds/"s;
+const auto UID_LED = "uid:blue"s;
}
namespace velia::system {
@@ -63,6 +65,35 @@
},
(CZECHLIGHT_SYSTEM_LEDS_MODULE_PREFIX + "*").c_str(),
SR_SUBSCR_PASSIVE | SR_SUBSCR_OPER_MERGE | SR_SUBSCR_CTX_REUSE);
+
+ const auto uidMaxBrightness = std::to_string(velia::utils::readFileInt64(m_sysfsLeds / UID_LED / "max_brightness"));
+ const auto triggerFile = m_sysfsLeds / UID_LED / "trigger";
+ const auto brightnessFile = m_sysfsLeds / UID_LED / "brightness";
+
+ m_srSubscribe->rpc_subscribe_tree(
+ (CZECHLIGHT_SYSTEM_LEDS_MODULE_PREFIX + "uid").c_str(),
+ [this, uidMaxBrightness, triggerFile, brightnessFile](auto session, auto, auto input, auto, auto, auto) {
+ std::string val = getValueAsString(getSubtree(input, (CZECHLIGHT_SYSTEM_LEDS_MODULE_PREFIX + "uid/state").c_str()));
+
+ try {
+ if (val == "on") {
+ utils::writeFile(triggerFile, "none");
+ utils::writeFile(brightnessFile, uidMaxBrightness);
+ } else if (val == "off") {
+ utils::writeFile(triggerFile, "none");
+ utils::writeFile(brightnessFile, "0");
+ } else if (val == "blinking") {
+ utils::writeFile(triggerFile, "timer");
+ utils::writeFile(brightnessFile, uidMaxBrightness);
+ }
+ } catch (const std::invalid_argument& e) {
+ m_log->warn("Failed to set state of the UID LED: '{}'", e.what());
+ session->set_error("Failed to set state of the UID LED", nullptr);
+ return SR_ERR_OPERATION_FAILED;
+ }
+
+ return SR_ERR_OK;
+ });
}
}
diff --git a/src/utils/io.cpp b/src/utils/io.cpp
index 4c84a89..5de62b2 100644
--- a/src/utils/io.cpp
+++ b/src/utils/io.cpp
@@ -78,6 +78,18 @@
return std::string(begin, end);
}
+void writeFile(const std::string& path, const std::string_view& contents)
+{
+ std::ofstream ofs(path);
+ if (!ofs.is_open()) {
+ throw std::invalid_argument("File '" + std::string(path) + "' could not be opened.");
+ }
+
+ if (!(ofs << contents)) {
+ throw std::invalid_argument("File '" + std::string(path) + "' could not be written.");
+ }
+}
+
void safeWriteFile(const std::string& filename, const std::string_view& contents)
{
auto throwErr = [&filename] (const auto& what) {
diff --git a/src/utils/io.h b/src/utils/io.h
index ec3286b..e2ab09b 100644
--- a/src/utils/io.h
+++ b/src/utils/io.h
@@ -17,5 +17,6 @@
int64_t readFileInt64(const std::filesystem::path& path);
std::vector<uint32_t> readFileWords(const std::filesystem::path& path, int valuesCount);
std::string readFileToString(const std::filesystem::path& path);
+void writeFile(const std::string& path, const std::string_view& contents);
void safeWriteFile(const std::string& filename, const std::string_view& contents);
}
diff --git a/tests/sysfs/leds/uid:blue/trigger b/tests/sysfs/leds/uid:blue/trigger
new file mode 100644
index 0000000..621e94f
--- /dev/null
+++ b/tests/sysfs/leds/uid:blue/trigger
@@ -0,0 +1 @@
+none
diff --git a/tests/sysrepo_system-leds.cpp b/tests/sysrepo_system-leds.cpp
index 5248faa..db648a4 100644
--- a/tests/sysrepo_system-leds.cpp
+++ b/tests/sysrepo_system-leds.cpp
@@ -1,10 +1,12 @@
#include "trompeloeil_doctest.h"
#include "dbus-helpers/dbus_rauc_server.h"
+#include "fs-helpers/utils.h"
#include "pretty_printers.h"
#include "system/LED.h"
#include "test_log_setup.h"
#include "test_sysrepo_helpers.h"
#include "tests/configure.cmake.h"
+#include "utils/io.h"
using namespace std::literals;
@@ -16,7 +18,10 @@
TEST_SYSREPO_INIT;
TEST_SYSREPO_INIT_CLIENT;
- auto fakeSysfsDir = std::filesystem::path {CMAKE_CURRENT_SOURCE_DIR + "/tests/sysfs/leds/"s};
+ auto fakeSysfsDir = std::filesystem::path {CMAKE_CURRENT_BINARY_DIR + "/tests/leds/"s};
+ removeDirectoryTreeIfExists(fakeSysfsDir);
+ std::filesystem::copy(CMAKE_CURRENT_SOURCE_DIR + "/tests/sysfs/leds"s, fakeSysfsDir, std::filesystem::copy_options::recursive);
+
velia::system::LED led(srConn, fakeSysfsDir);
std::this_thread::sleep_for(10ms);
@@ -35,4 +40,46 @@
{"/led[name='uid:red']/brightness", "100"},
{"/led[name='uid:red']/name", "uid:red"},
});
+
+ std::shared_ptr<sysrepo::Vals> rpcInput = std::make_shared<sysrepo::Vals>(1);
+ std::string state;
+ std::string expectedTrigger;
+ std::string expectedBrightness;
+
+ /* This isn't what actually happens in real-life. The contents of the trigger file is usually something like this (i.e., list of available triggers).
+ *
+ * [none] kbd-scrolllock kbd-numlock kbd-capslock kbd-kanalock kbd-shiftlock kbd-altgrlock kbd-ctrllock kbd-altlock kbd-shiftllock kbd-shiftrlock kbd-ctrlllock kbd-ctrlrlock mmc0 timer oneshot heartbeat gpio default-on transient panic netdev f1072004.mdio-mii:01:link f1072004.mdio-mii:01:1Gbps f1072004.mdio-mii:01:100Mbps f1072004.mdio-mii:01:10Mbps f1072004.mdio-mii:00:link f1072004.mdio-mii:00:1Gbps f1072004.mdio-mii:00:100Mbps f1072004.mdio-mii:00:10Mbps
+ *
+ * The value enclosed in brackets is the current active trigger. You can change it by writing a value corresponding to a trigger to the trigger file.
+ * I'm not going to simulate sysfs led behaviour here, so just test that the original contents was "none" and the value written by the RPC is the expected value.
+ * Also, I'm not implementing the 'timer' trigger behaviour, so the value written to the brightness file is static.
+ */
+ REQUIRE(velia::utils::readFileString(fakeSysfsDir / "uid:blue" / "trigger") == "none");
+
+ SECTION("UID led on")
+ {
+ state = "on";
+ expectedTrigger = "none";
+ expectedBrightness = "256";
+ }
+
+ SECTION("UID led off")
+ {
+ state = "off";
+ expectedTrigger = "none";
+ expectedBrightness = "0";
+ }
+
+ SECTION("UID led blinking")
+ {
+ state = "blinking";
+ expectedTrigger = "timer";
+ expectedBrightness = "256";
+ }
+
+ rpcInput->val(0)->set("/czechlight-system:leds/uid/state", state.c_str());
+ auto res = client->rpc_send("/czechlight-system:leds/uid", rpcInput);
+ REQUIRE(res->val_cnt() == 0);
+ REQUIRE(velia::utils::readFileString(fakeSysfsDir / "uid:blue" / "trigger") == expectedTrigger);
+ REQUIRE(velia::utils::readFileString(fakeSysfsDir / "uid:blue" / "brightness") == expectedBrightness);
}
diff --git a/yang/czechlight-system@2021-01-13.yang b/yang/czechlight-system@2021-01-13.yang
index afd6c80..bd8449e 100644
--- a/yang/czechlight-system@2021-01-13.yang
+++ b/yang/czechlight-system@2021-01-13.yang
@@ -236,6 +236,20 @@
type percent;
}
}
+
+ action uid {
+ input {
+ leaf state {
+ mandatory true;
+ description "Change state of the UID led (turn off, on, or keep blinking).";
+ type enumeration {
+ enum off;
+ enum on;
+ enum blinking;
+ }
+ }
+ }
+ }
}
deviation /sys:system-shutdown { deviate not-supported; }