sysrepo: Implement parts of ietf-system module
Implement announcing OS name and OS release through ietf-system YANG
model [1], specifically via its top-level container system-state.
The properties are obtained from /etc/os-release file.
[1] https://tools.ietf.org/html/rfc7317
Change-Id: I82d5fd54659ea365232a3a9455dd73f84b8fd0d1
diff --git a/src/system/Sysrepo.cpp b/src/system/Sysrepo.cpp
new file mode 100644
index 0000000..c9ce0fa
--- /dev/null
+++ b/src/system/Sysrepo.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2021 CESNET, https://photonics.cesnet.cz/
+ *
+ * Written by Tomáš Pecka <tomas.pecka@fit.cvut.cz>
+ *
+ */
+
+#include <boost/algorithm/string/predicate.hpp>
+#include <fstream>
+#include "Sysrepo.h"
+#include "utils/io.h"
+#include "utils/log.h"
+
+using namespace std::literals;
+
+namespace {
+
+const auto IETF_SYSTEM_MODULE_NAME = "ietf-system"s;
+const auto IETF_SYSTEM_STATE_MODULE_PREFIX = "/"s + IETF_SYSTEM_MODULE_NAME + ":system-state/"s;
+
+/** @brief Returns key=value pairs from (e.g. /etc/os-release) as a std::map */
+std::map<std::string, std::string> parseKeyValueFile(const std::filesystem::path& path)
+{
+ std::map<std::string, std::string> res;
+ std::ifstream ifs(path);
+ if (!ifs.is_open())
+ throw std::invalid_argument("File '" + std::string(path) + "' not found.");
+
+ std::string line;
+ while (std::getline(ifs, line)) {
+ // man os-release: Lines beginning with "#" shall be ignored as comments. Blank lines are permitted and ignored.
+ if (line.empty() || boost::algorithm::starts_with(line, "#")) {
+ continue;
+ }
+
+ size_t equalSignPos = line.find_first_of('=');
+ if (equalSignPos != std::string::npos) {
+ std::string key = line.substr(0, equalSignPos);
+ std::string val = line.substr(equalSignPos + 1);
+
+ // remove quotes from value
+ if (val.length() >= 2 && val.front() == '"' && val.front() == val.back()) {
+ val = val.substr(1, val.length() - 2);
+ }
+
+ res[key] = val;
+ } else { // when there is no = sign, treat the value as empty string
+ res[line] = "";
+ }
+ }
+
+ return res;
+}
+
+}
+
+namespace velia::system {
+
+/** @brief Reads some OS-identification data from osRelease file and publishes them via ietf-system model */
+Sysrepo::Sysrepo(std::shared_ptr<::sysrepo::Session> srSession, const std::filesystem::path& osRelease)
+ : m_srSession(std::move(srSession))
+ , m_log(spdlog::get("system"))
+{
+ std::map<std::string, std::string> osReleaseContents = parseKeyValueFile(osRelease);
+
+ std::map<std::string, std::string> opsSystemStateData {
+ {IETF_SYSTEM_STATE_MODULE_PREFIX + "platform/os-name", osReleaseContents.at("NAME")},
+ {IETF_SYSTEM_STATE_MODULE_PREFIX + "platform/os-release", osReleaseContents.at("VERSION")},
+ {IETF_SYSTEM_STATE_MODULE_PREFIX + "platform/os-version", osReleaseContents.at("VERSION")},
+ };
+
+ sr_datastore_t oldDatastore = m_srSession->session_get_ds();
+ m_srSession->session_switch_ds(SR_DS_OPERATIONAL);
+
+ for (const auto& [k, v] : opsSystemStateData) {
+ m_log->debug("Pushing to sysrepo: {} = {}", k, v);
+ m_srSession->set_item_str(k.c_str(), v.c_str());
+ }
+
+ m_srSession->apply_changes();
+ m_srSession->session_switch_ds(oldDatastore);
+}
+}
diff --git a/src/system/Sysrepo.h b/src/system/Sysrepo.h
new file mode 100644
index 0000000..0aeda9a
--- /dev/null
+++ b/src/system/Sysrepo.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2021 CESNET, https://photonics.cesnet.cz/
+ *
+ * Written by Tomáš Pecka <tomas.pecka@fit.cvut.cz>
+ *
+ */
+#pragma once
+
+#include <filesystem>
+#include <sysrepo-cpp/Session.hpp>
+#include "utils/log-fwd.h"
+
+namespace velia::system {
+
+class Sysrepo {
+public:
+ explicit Sysrepo(std::shared_ptr<::sysrepo::Session> srSession, const std::filesystem::path& osRelease);
+
+private:
+ std::shared_ptr<::sysrepo::Session> m_srSession;
+ velia::Log m_log;
+};
+}