blob: c051d72458474452ed0457335555be609ff41052 [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"
13#include "utils/log.h"
14
15using namespace std::string_literals;
16
17static const auto UPLOAD_URL_CONTAINER = "/czechlight-system:journal-upload";
18
19namespace {
20
21std::optional<std::string> extractURL(sysrepo::Session session)
22{
23 auto data = session.getData(UPLOAD_URL_CONTAINER);
24 if (!data) {
25 return std::nullopt;
26 }
27
28 std::string url;
29
30 auto hostNode = data->findPath(UPLOAD_URL_CONTAINER + "/host"s);
31 auto hostValue = std::string{hostNode->asTerm().valueStr()};
32 auto isIpv6 = hostNode->asTerm().valueType().internalPluginId().find("ipv6") != std::string::npos;
33
34 url += data->findPath(UPLOAD_URL_CONTAINER + "/protocol"s)->asTerm().valueStr();
35 url += "://";
36
37 if (isIpv6) {
38 url += "[" + hostValue + "]";
39 } else {
40 url += hostValue;
41 }
42
43 url += ":";
44 url += std::string(data->findPath(UPLOAD_URL_CONTAINER + "/port"s)->asTerm().valueStr());
45
46 return url;
47}
48
49sysrepo::ErrorCode configureJournalUpload(velia::Log log, const std::optional<std::string>& url, const std::filesystem::path& envFile, const velia::system::JournalUpload::RestartCb& restartCb)
50{
51 std::optional<std::string> oldContent;
52 std::optional<std::string> newContent;
53
54 try {
55 oldContent = velia::utils::readFileToString(envFile);
56 } catch (const std::invalid_argument&) {
57 // if the file does not exist, oldContent will still be nullopt
58 }
59
60 if (url) {
61 newContent = "DESTINATION=" + *url + '\n';
62 }
63
64 if (oldContent != newContent) {
65 if (newContent) {
66 std::ofstream ofs(envFile);
67 ofs << *newContent;
68
69 newContent->pop_back(); // remove the \n
70 log->trace("systemd-journal-upload.service environment file {} set to {}", std::string(envFile), *newContent);
71 } else {
72 std::filesystem::remove(envFile);
73 log->trace("systemd-journal-upload.service environment file {} removed", std::string(envFile));
74 }
75
76 restartCb(log);
77 }
78
79 return sysrepo::ErrorCode::Ok;
80}
81
82sysrepo::Subscription journalUploadSubscription(velia::Log log, sysrepo::Session session, const std::filesystem::path& envFile, const velia::system::JournalUpload::RestartCb& restartCb)
83{
84 auto sub = session.onModuleChange(
85 "czechlight-system",
86 [log, envFile, restartCb](auto session, auto, auto, auto, auto, auto) {
87 return configureJournalUpload(log, extractURL(session), envFile, restartCb);
88 },
89 std::nullopt,
90 0,
91 sysrepo::SubscribeOptions::DoneOnly | sysrepo::SubscribeOptions::Enabled);
92
93 /*
94 * 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.
95 * Therefore, first register the callback and than call the configure manually.
96 * The configure function does not restart the service unless the configuration file content changes so this should not trigger the unit restart.
97 */
98 configureJournalUpload(log, extractURL(session), envFile, restartCb);
99
100 return sub;
101}
102}
103
104namespace velia::system {
105
106JournalUpload::JournalUpload(sysrepo::Session session, const std::filesystem::path& envFile, const RestartCb& restartCb)
107 : m_log(spdlog::get("system"))
108 , m_srSub(journalUploadSubscription(m_log, session, envFile, restartCb))
109{
110}
111
112}