blob: e3c20e9ec537865d8c5f7b2da115d2109c45de8a [file] [log] [blame]
Tomáš Peckae566fd42024-01-02 17:00:10 +01001/*
2 * Copyright (C) 2024 CESNET, https://photonics.cesnet.cz/
3 *
4 * Written by Tomáš Pecka <tomas.pecka@cesnet.cz>
5 *
6 */
7
8#include <filesystem>
9#include <regex>
10#include <sysrepo-cpp/Enum.hpp>
11#include "JournalUpload.h"
12#include "utils/io.h"
Jan Kundrát7935e812024-09-17 17:02:01 +020013#include "utils/libyang.h"
Tomáš Peckae566fd42024-01-02 17:00:10 +010014#include "utils/log.h"
15
16using namespace std::string_literals;
17
18static const auto UPLOAD_URL_CONTAINER = "/czechlight-system:journal-upload";
19
20namespace {
21
22std::optional<std::string> extractURL(sysrepo::Session session)
23{
24 auto data = session.getData(UPLOAD_URL_CONTAINER);
25 if (!data) {
26 return std::nullopt;
27 }
28
29 std::string url;
30
31 auto hostNode = data->findPath(UPLOAD_URL_CONTAINER + "/host"s);
Jan Kundrát7935e812024-09-17 17:02:01 +020032 auto hostValue = velia::utils::getValueAsString(*hostNode);
Tomáš Peckae566fd42024-01-02 17:00:10 +010033 auto isIpv6 = hostNode->asTerm().valueType().internalPluginId().find("ipv6") != std::string::npos;
34
35 url += data->findPath(UPLOAD_URL_CONTAINER + "/protocol"s)->asTerm().valueStr();
36 url += "://";
37
38 if (isIpv6) {
39 url += "[" + hostValue + "]";
40 } else {
41 url += hostValue;
42 }
43
44 url += ":";
Jan Kundrát7935e812024-09-17 17:02:01 +020045 url += data->findPath(UPLOAD_URL_CONTAINER + "/port"s)->asTerm().valueStr();
Tomáš Peckae566fd42024-01-02 17:00:10 +010046
47 return url;
48}
49
50sysrepo::ErrorCode configureJournalUpload(velia::Log log, const std::optional<std::string>& url, const std::filesystem::path& envFile, const velia::system::JournalUpload::RestartCb& restartCb)
51{
52 std::optional<std::string> oldContent;
53 std::optional<std::string> newContent;
54
55 try {
56 oldContent = velia::utils::readFileToString(envFile);
57 } catch (const std::invalid_argument&) {
58 // if the file does not exist, oldContent will still be nullopt
59 }
60
61 if (url) {
62 newContent = "DESTINATION=" + *url + '\n';
63 }
64
65 if (oldContent != newContent) {
66 if (newContent) {
67 std::ofstream ofs(envFile);
68 ofs << *newContent;
69
70 newContent->pop_back(); // remove the \n
71 log->trace("systemd-journal-upload.service environment file {} set to {}", std::string(envFile), *newContent);
72 } else {
73 std::filesystem::remove(envFile);
74 log->trace("systemd-journal-upload.service environment file {} removed", std::string(envFile));
75 }
76
77 restartCb(log);
78 }
79
80 return sysrepo::ErrorCode::Ok;
81}
82
83sysrepo::Subscription journalUploadSubscription(velia::Log log, sysrepo::Session session, const std::filesystem::path& envFile, const velia::system::JournalUpload::RestartCb& restartCb)
84{
85 auto sub = session.onModuleChange(
86 "czechlight-system",
87 [log, envFile, restartCb](auto session, auto, auto, auto, auto, auto) {
88 return configureJournalUpload(log, extractURL(session), envFile, restartCb);
89 },
90 std::nullopt,
91 0,
92 sysrepo::SubscribeOptions::DoneOnly | sysrepo::SubscribeOptions::Enabled);
93
94 /*
95 * In case someone removes the presence container between sysrepo loads the data and this module startup we won't get a change (Deleted) from sysrepo and the file won't be written.
96 * Therefore, first register the callback and than call the configure manually.
97 * The configure function does not restart the service unless the configuration file content changes so this should not trigger the unit restart.
98 */
99 configureJournalUpload(log, extractURL(session), envFile, restartCb);
100
101 return sub;
102}
103}
104
105namespace velia::system {
106
107JournalUpload::JournalUpload(sysrepo::Session session, const std::filesystem::path& envFile, const RestartCb& restartCb)
108 : m_log(spdlog::get("system"))
109 , m_srSub(journalUploadSubscription(m_log, session, envFile, restartCb))
110{
111}
112
113}