blob: e508d205828a6834bb9c903efc9ec77058c0e2a1 [file] [log] [blame]
Tomáš Pecka292bc9c2021-01-11 22:03:11 +01001/*
2 * Copyright (C) 2021 CESNET, https://photonics.cesnet.cz/
3 *
4 * Written by Tomáš Pecka <tomas.pecka@fit.cvut.cz>
5 *
6 */
7
8#include <boost/algorithm/string/predicate.hpp>
9#include <fstream>
Tomáš Peckaf976c5b2021-01-23 21:19:52 +010010#include "IETFSystem.h"
Tomáš Pecka879a6032021-02-03 17:21:48 +010011#include "system_vars.h"
12#include "utils/exec.h"
Tomáš Pecka292bc9c2021-01-11 22:03:11 +010013#include "utils/io.h"
14#include "utils/log.h"
Tomáš Pecka272abaf2021-01-24 12:28:43 +010015#include "utils/sysrepo.h"
Tomáš Pecka292bc9c2021-01-11 22:03:11 +010016
17using namespace std::literals;
18
19namespace {
20
21const auto IETF_SYSTEM_MODULE_NAME = "ietf-system"s;
22const auto IETF_SYSTEM_STATE_MODULE_PREFIX = "/"s + IETF_SYSTEM_MODULE_NAME + ":system-state/"s;
23
24/** @brief Returns key=value pairs from (e.g. /etc/os-release) as a std::map */
25std::map<std::string, std::string> parseKeyValueFile(const std::filesystem::path& path)
26{
27 std::map<std::string, std::string> res;
28 std::ifstream ifs(path);
29 if (!ifs.is_open())
30 throw std::invalid_argument("File '" + std::string(path) + "' not found.");
31
32 std::string line;
33 while (std::getline(ifs, line)) {
34 // man os-release: Lines beginning with "#" shall be ignored as comments. Blank lines are permitted and ignored.
35 if (line.empty() || boost::algorithm::starts_with(line, "#")) {
36 continue;
37 }
38
39 size_t equalSignPos = line.find_first_of('=');
40 if (equalSignPos != std::string::npos) {
41 std::string key = line.substr(0, equalSignPos);
42 std::string val = line.substr(equalSignPos + 1);
43
44 // remove quotes from value
45 if (val.length() >= 2 && val.front() == '"' && val.front() == val.back()) {
46 val = val.substr(1, val.length() - 2);
47 }
48
49 res[key] = val;
50 } else { // when there is no = sign, treat the value as empty string
51 res[line] = "";
52 }
53 }
54
55 return res;
56}
57
58}
59
60namespace velia::system {
61
62/** @brief Reads some OS-identification data from osRelease file and publishes them via ietf-system model */
Tomáš Peckaf976c5b2021-01-23 21:19:52 +010063IETFSystem::IETFSystem(std::shared_ptr<::sysrepo::Session> srSession, const std::filesystem::path& osRelease)
Tomáš Pecka292bc9c2021-01-11 22:03:11 +010064 : m_srSession(std::move(srSession))
Tomáš Pecka879a6032021-02-03 17:21:48 +010065 , m_srSubscribe(std::make_shared<::sysrepo::Subscribe>(m_srSession))
Tomáš Pecka292bc9c2021-01-11 22:03:11 +010066 , m_log(spdlog::get("system"))
67{
68 std::map<std::string, std::string> osReleaseContents = parseKeyValueFile(osRelease);
69
70 std::map<std::string, std::string> opsSystemStateData {
71 {IETF_SYSTEM_STATE_MODULE_PREFIX + "platform/os-name", osReleaseContents.at("NAME")},
72 {IETF_SYSTEM_STATE_MODULE_PREFIX + "platform/os-release", osReleaseContents.at("VERSION")},
73 {IETF_SYSTEM_STATE_MODULE_PREFIX + "platform/os-version", osReleaseContents.at("VERSION")},
74 };
75
Tomáš Pecka272abaf2021-01-24 12:28:43 +010076 utils::valuesPush(opsSystemStateData, m_srSession, SR_DS_OPERATIONAL);
Tomáš Pecka879a6032021-02-03 17:21:48 +010077
78 m_srSubscribe->rpc_subscribe(
79 ("/" + IETF_SYSTEM_MODULE_NAME + ":system-restart").c_str(),
80 [this](::sysrepo::S_Session session, [[maybe_unused]] const char* op_path, [[maybe_unused]] const ::sysrepo::S_Vals input, [[maybe_unused]] sr_event_t event, [[maybe_unused]] uint32_t request_id, [[maybe_unused]] ::sysrepo::S_Vals_Holder output) {
81 try {
82 velia::utils::execAndWait(m_log, SYSTEMCTL_EXECUTABLE, {"reboot"}, "", {});
83 } catch(const std::runtime_error& e) {
84 session->set_error("Reboot procedure failed.", nullptr);
85 return SR_ERR_OPERATION_FAILED;
86 }
87
88 return SR_ERR_OK;
89 },
90 0,
91 SR_SUBSCR_CTX_REUSE);
Tomáš Pecka292bc9c2021-01-11 22:03:11 +010092}
93}