blob: 6f060ab2c000556b96393cca677dd919bb413831 [file] [log] [blame]
Tomáš Pecka39bbb512021-05-23 22:07:22 +02001/*
2 * Copyright (C) 2021 CESNET, https://photonics.cesnet.cz/
3 *
4 * Written by Tomáš Pecka <tomas.pecka@cesnet.cz>
5 *
6 */
7
8#include <numeric>
9#include "IETFInterfacesConfig.h"
10#include "utils/io.h"
11#include "utils/libyang.h"
12#include "utils/log.h"
13#include "utils/sysrepo.h"
14
15using namespace std::string_literals;
16
17namespace {
18
19const auto CZECHLIGHT_NETWORK_MODULE_NAME = "czechlight-network"s;
20const auto IETF_IP_MODULE_NAME = "ietf-ip"s;
21const auto IETF_INTERFACES_MODULE_NAME = "ietf-interfaces"s;
22const auto IETF_ROUTING_MODULE_NAME = "ietf-routing"s;
23const auto IETF_IPV4_UNICAST_ROUTING_MODULE_NAME = "ietf-ipv4-unicast-routing";
24const auto IETF_IPV6_UNICAST_ROUTING_MODULE_NAME = "ietf-ipv6-unicast-routing";
25const auto IETF_INTERFACES = "/"s + IETF_INTERFACES_MODULE_NAME + ":interfaces"s;
26
27std::string generateNetworkConfigFile(const std::string& linkName, const std::map<std::string, std::vector<std::string>>& values)
28{
29 std::ostringstream oss;
30
31 oss << "[Match]" << std::endl;
32 oss << "Name=" << linkName << std::endl;
33
34 for (const auto& [sectionName, values] : values) {
35 oss << std::endl
36 << '[' << sectionName << ']' << std::endl;
37
38 for (const auto& confValue : values) {
39 oss << confValue << std::endl;
40 }
41 }
42
43 return oss.str();
44}
45
46}
47
48namespace velia::system {
49
50IETFInterfacesConfig::IETFInterfacesConfig(std::shared_ptr<::sysrepo::Session> srSess, std::filesystem::path configDirectory, std::vector<std::string> managedLinks, reload_cb_t reloadCallback)
51 : m_log(spdlog::get("system"))
52 , m_reloadCb(std::move(reloadCallback))
53 , m_configDirectory(std::move(configDirectory))
54 , m_managedLinks(std::move(managedLinks))
55 , m_srSession(std::move(srSess))
56 , m_srSubscribe(std::make_shared<::sysrepo::Subscribe>(m_srSession))
57{
58 utils::ensureModuleImplemented(m_srSession, IETF_INTERFACES_MODULE_NAME, "2018-02-20");
59 utils::ensureModuleImplemented(m_srSession, IETF_IP_MODULE_NAME, "2018-02-22");
60 utils::ensureModuleImplemented(m_srSession, IETF_ROUTING_MODULE_NAME, "2018-03-13");
61 utils::ensureModuleImplemented(m_srSession, IETF_IPV4_UNICAST_ROUTING_MODULE_NAME, "2018-03-13");
62 utils::ensureModuleImplemented(m_srSession, IETF_IPV6_UNICAST_ROUTING_MODULE_NAME, "2018-03-13");
63 utils::ensureModuleImplemented(m_srSession, CZECHLIGHT_NETWORK_MODULE_NAME, "2021-02-22");
64
65 m_srSubscribe->module_change_subscribe(
66 IETF_INTERFACES_MODULE_NAME.c_str(), [this](auto session, auto, auto, auto, auto) { return moduleChange(session); }, IETF_INTERFACES.c_str(), 0, SR_SUBSCR_DONE_ONLY);
67}
68
69int IETFInterfacesConfig::moduleChange(std::shared_ptr<::sysrepo::Session> session) const
70{
71 std::map<std::string, std::string> networkConfigFiles;
72
73 if (auto data = session->get_data("/ietf-interfaces:interfaces/interface"); data) {
74 auto linkEntries = data->find_path("/ietf-interfaces:interfaces/interface");
75 for (const auto& linkEntry : linkEntries->data()) {
76 std::map<std::string, std::vector<std::string>> configValues;
77
78 auto linkName = getValueAsString(getSubtree(linkEntry, "name"));
79
80 if (auto set = linkEntry->find_path("description"); set->number() != 0) {
81 configValues["Network"].push_back("Description="s + getValueAsString(set->data().front()));
82 }
83
84 configValues["Network"].push_back("Bridge=br0");
85 configValues["Network"].push_back("LLDP=true");
86 configValues["Network"].push_back("EmitLLDP=nearest-bridge");
87
88 networkConfigFiles[linkName] = generateNetworkConfigFile(linkName, configValues);
89 }
90 }
91
92 auto changedLinks = updateNetworkFiles(networkConfigFiles, m_configDirectory);
93 m_reloadCb(changedLinks);
94 return SR_ERR_OK;
95}
96
97std::vector<std::string> IETFInterfacesConfig::updateNetworkFiles(const std::map<std::string, std::string>& networkConfig, const std::filesystem::path& configDir) const
98{
99 std::vector<std::string> changedLinks;
100
101 for (const auto& link : m_managedLinks) {
102 const auto targetFile = configDir / (link + ".network");
103 const bool fileExists = std::filesystem::exists(targetFile);
104 const bool updateExists = networkConfig.contains(link);
105
106 // no configuration for link present and the file doesn't even exist -> keep default configuration
107 if (!fileExists && !updateExists) {
108 continue;
109 }
110
111 // configuration for link present, file exists, but it has the same content as the new one -> keep current configuration, no need to rewrite
112 if (fileExists && updateExists && velia::utils::readFileToString(targetFile) == networkConfig.at(link)) {
113 continue;
114 }
115
116 if (updateExists) {
117 velia::utils::safeWriteFile(targetFile, networkConfig.at(link));
118 } else { // configuration removed
119 std::filesystem::remove(targetFile);
120 }
121
122 changedLinks.push_back(link);
123 }
124
125 return changedLinks;
126}
127
128}