blob: c09a1e86068f4c1a835871119fa1a0cf2fca250c [file] [log] [blame]
Tomáš Peckaf10b9302021-02-23 19:02:02 +01001/*
2 * Copyright (C) 2021 CESNET, https://photonics.cesnet.cz/
3 *
4 * Written by Tomáš Pecka <tomas.pecka@cesnet.cz>
5 *
6*/
7
8#include "trompeloeil_doctest.h"
Tomáš Pecka39bbb512021-05-23 22:07:22 +02009#include <filesystem>
Václav Kubernát7efd6d52021-11-09 01:31:11 +010010#include <sysrepo-cpp/utils/exception.hpp>
Tomáš Peckaf10b9302021-02-23 19:02:02 +010011#include "pretty_printers.h"
12#include "system/IETFInterfaces.h"
Tomáš Pecka39bbb512021-05-23 22:07:22 +020013#include "system/IETFInterfacesConfig.h"
Tomáš Peckaf10b9302021-02-23 19:02:02 +010014#include "test_log_setup.h"
15#include "test_sysrepo_helpers.h"
Tomáš Pecka39bbb512021-05-23 22:07:22 +020016#include "tests/configure.cmake.h"
Tomáš Peckaf10b9302021-02-23 19:02:02 +010017#include "tests/mock/system.h"
Tomáš Pecka39bbb512021-05-23 22:07:22 +020018#include "utils/io.h"
Tomáš Peckaf10b9302021-02-23 19:02:02 +010019
Tomáš Pecka71a98b02021-05-17 15:59:40 +020020using namespace std::string_literals;
21
Tomáš Peckaf10b9302021-02-23 19:02:02 +010022TEST_CASE("ietf-interfaces localhost")
23{
24 TEST_SYSREPO_INIT_LOGS;
25 TEST_SYSREPO_INIT;
26 TEST_SYSREPO_INIT_CLIENT;
27
28 auto network = std::make_shared<velia::system::IETFInterfaces>(srSess);
29
30 // We didn't came up with some way of mocking netlink. At least check that there is the loopback
31 // interface with expected values. It is *probably* safe to assume that there is at least the lo device.
Václav Kubernát7efd6d52021-11-09 01:31:11 +010032 auto lo = dataFromSysrepo(client, "/ietf-interfaces:interfaces/interface[name='lo']", sysrepo::Datastore::Operational);
Tomáš Pecka70e54562021-03-10 12:39:03 +010033
34 // ensure statistics keys exist and remove them ; we can't really predict the content
Václav Kubernát7efd6d52021-11-09 01:31:11 +010035 for (const auto& key : {"/statistics/in-discards", "/statistics/in-errors", "/statistics/in-octets", "/statistics/out-discards", "/statistics/out-errors", "/statistics/out-octets"}) {
Tomáš Pecka70e54562021-03-10 12:39:03 +010036 auto it = lo.find(key);
37 REQUIRE(it != lo.end());
38 lo.erase(it);
39 }
40
Tomáš Peckabd43a942021-08-04 10:12:49 +020041 REQUIRE(lo == std::map<std::string, std::string>{
Tomáš Peckaf10b9302021-02-23 19:02:02 +010042 {"/name", "lo"},
43 {"/type", "iana-if-type:softwareLoopback"},
44 {"/phys-address", "00:00:00:00:00:00"},
45 {"/oper-status", "unknown"},
Tomáš Pecka09729382021-03-08 19:36:50 +010046 {"/ietf-ip:ipv4", ""},
47 {"/ietf-ip:ipv4/address[ip='127.0.0.1']", ""},
48 {"/ietf-ip:ipv4/address[ip='127.0.0.1']/ip", "127.0.0.1"},
49 {"/ietf-ip:ipv4/address[ip='127.0.0.1']/prefix-length", "8"},
50 {"/ietf-ip:ipv6", ""},
Tomáš Peckabd43a942021-08-04 10:12:49 +020051 {"/ietf-ip:ipv6/autoconf", ""},
Tomáš Pecka09729382021-03-08 19:36:50 +010052 {"/ietf-ip:ipv6/address[ip='::1']", ""},
53 {"/ietf-ip:ipv6/address[ip='::1']/ip", "::1"},
54 {"/ietf-ip:ipv6/address[ip='::1']/prefix-length", "128"},
Václav Kubernát7efd6d52021-11-09 01:31:11 +010055 {"/statistics", ""},
Tomáš Peckaf10b9302021-02-23 19:02:02 +010056 });
Tomáš Pecka3663aae2021-03-10 13:46:31 +010057 // NOTE: There are no neighbours on loopback
Tomáš Pecka4c7ff2a2021-08-06 15:05:54 +020058}
59
60struct FakeNetworkReload {
61public:
62 MAKE_CONST_MOCK1(cb, void(const std::vector<std::string>&));
63};
64
65TEST_CASE("Config data in ietf-interfaces")
66{
67 TEST_SYSREPO_INIT_LOGS;
68 TEST_SYSREPO_INIT;
69 TEST_SYSREPO_INIT_CLIENT;
70 trompeloeil::sequence seq1;
71
Tomáš Pecka9cb218c2022-06-13 11:48:20 +020072 sysrepo::Connection{}.sessionStart(sysrepo::Datastore::Running).copyConfig(sysrepo::Datastore::Startup, "ietf-interfaces");
73
Tomáš Pecka4c7ff2a2021-08-06 15:05:54 +020074 auto fake = FakeNetworkReload();
75
76 const auto fakeConfigDir = std::filesystem::path(CMAKE_CURRENT_BINARY_DIR) / "tests/network/"s;
77 std::filesystem::remove_all(fakeConfigDir);
78 std::filesystem::create_directories(fakeConfigDir);
79
80 auto network = std::make_shared<velia::system::IETFInterfacesConfig>(srSess, fakeConfigDir, std::vector<std::string>{"br0", "eth0", "eth1"}, [&fake](const std::vector<std::string>& updatedInterfaces) { fake.cb(updatedInterfaces); });
Tomáš Pecka71a98b02021-05-17 15:59:40 +020081
82 SECTION("Link changes disabled")
83 {
Tomáš Pecka71a98b02021-05-17 15:59:40 +020084 SECTION("Only specified link names can appear in configurable datastore")
85 {
86 for (const auto& [name, type] : {std::pair<const char*, const char*>{"eth0", "iana-if-type:ethernetCsmacd"},
87 {"eth1", "iana-if-type:ethernetCsmacd"},
88 {"br0", "iana-if-type:bridge"},
89 {"osc", "iana-if-type:ethernetCsmacd"},
90 {"oscW", "iana-if-type:ethernetCsmacd"},
91 {"oscE", "iana-if-type:ethernetCsmacd"}}) {
Jan Kundrátb3e99982022-03-18 17:38:20 +010092 client.setItem("/ietf-interfaces:interfaces/interface[name='"s + name + "']/type", type);
93 client.setItem("/ietf-interfaces:interfaces/interface[name='"s + name + "']/enabled", "false");
Tomáš Pecka71a98b02021-05-17 15:59:40 +020094 }
Tomáš Pecka9cb218c2022-06-13 11:48:20 +020095
96 REQUIRE_CALL(fake, cb(std::vector<std::string>{"br0", "eth0", "eth1"})).IN_SEQUENCE(seq1); // only these are monitored by the test
Václav Kubernát7efd6d52021-11-09 01:31:11 +010097 client.applyChanges();
Tomáš Pecka71a98b02021-05-17 15:59:40 +020098 }
99
100 SECTION("Invalid type for a valid link")
101 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100102 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/type", "iana-if-type:softwareLoopback");
103 REQUIRE_THROWS_AS(client.applyChanges(), sysrepo::ErrorWithCode);
Tomáš Pecka71a98b02021-05-17 15:59:40 +0200104 }
105
106 SECTION("Invalid name")
107 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100108 client.setItem("/ietf-interfaces:interfaces/interface[name='blah0']/type", "iana-if-type:ethernetCsmacd");
109 REQUIRE_THROWS_AS(client.applyChanges(), sysrepo::ErrorWithCode);
Tomáš Pecka71a98b02021-05-17 15:59:40 +0200110 }
111 }
Tomáš Peckade0ef2f2021-07-08 13:23:50 +0200112
113 SECTION("There must always be enabled protocol or the interface must be explicitely disabled")
114 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100115 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/type", "iana-if-type:ethernetCsmacd");
Tomáš Peckade0ef2f2021-07-08 13:23:50 +0200116
117 SECTION("Disabled protocols; enabled link")
118 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100119 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/enabled", "true");
120 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:enabled", "false");
121 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:enabled", "false");
122 REQUIRE_THROWS_AS(client.applyChanges(), sysrepo::ErrorWithCode);
Tomáš Peckade0ef2f2021-07-08 13:23:50 +0200123 }
124
125 SECTION("Active protocols; disabled link")
126 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100127 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/enabled", "false");
128 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:enabled", "false");
129 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:enabled", "true");
130 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:address[ip='2001:db8::1']/ietf-ip:prefix-length", "32");
Tomáš Pecka4c7ff2a2021-08-06 15:05:54 +0200131 REQUIRE_CALL(fake, cb(std::vector<std::string>{"eth0"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100132 client.applyChanges();
Tomáš Peckade0ef2f2021-07-08 13:23:50 +0200133 }
134
135 SECTION("IPv4 only; enabled link")
136 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100137 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/enabled", "true");
138 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:enabled", "true");
139 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24");
140 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:enabled", "false");
Tomáš Pecka4c7ff2a2021-08-06 15:05:54 +0200141 REQUIRE_CALL(fake, cb(std::vector<std::string>{"eth0"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100142 client.applyChanges();
Tomáš Peckade0ef2f2021-07-08 13:23:50 +0200143 }
144 }
145
146 SECTION("Every active protocol must have at least one IP address assigned")
147 {
Tomáš Pecka9cb218c2022-06-13 11:48:20 +0200148 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/type", "iana-if-type:ethernetCsmacd");
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100149 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/enabled", "false");
Tomáš Pecka4c7ff2a2021-08-06 15:05:54 +0200150 REQUIRE_CALL(fake, cb(std::vector<std::string>{"eth0"})).IN_SEQUENCE(seq1).TIMES(AT_MOST(1));
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100151 client.applyChanges();
Tomáš Peckade0ef2f2021-07-08 13:23:50 +0200152
153 SECTION("Enabled IPv4 protocol with some IPs assigned is valid")
154 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100155 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/enabled", "true");
156 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:enabled", "true");
157 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24");
158 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.2']/ietf-ip:prefix-length", "24");
159 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:enabled", "false");
Tomáš Pecka4c7ff2a2021-08-06 15:05:54 +0200160 REQUIRE_CALL(fake, cb(std::vector<std::string>{"eth0"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100161 client.applyChanges();
Tomáš Peckade0ef2f2021-07-08 13:23:50 +0200162 }
163
164 SECTION("Enabled IPv6 protocol with some IPs assigned is valid")
165 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100166 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/enabled", "true");
167 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:enabled", "false");
168 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:enabled", "true");
169 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:address[ip='2001:db8::1']/ietf-ip:prefix-length", "32");
Tomáš Pecka4c7ff2a2021-08-06 15:05:54 +0200170 REQUIRE_CALL(fake, cb(std::vector<std::string>{"eth0"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100171 client.applyChanges();
Tomáš Peckade0ef2f2021-07-08 13:23:50 +0200172 }
173
Tomáš Peckabd43a942021-08-04 10:12:49 +0200174 SECTION("Enabled IPv4 protocol must have at least one IP or the autoconfiguration must be on")
Tomáš Peckade0ef2f2021-07-08 13:23:50 +0200175 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100176 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/enabled", "true");
177 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:enabled", "true");
178 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "false");
179 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:enabled", "false");
180 REQUIRE_THROWS_AS(client.applyChanges(), sysrepo::ErrorWithCode);
Tomáš Peckade0ef2f2021-07-08 13:23:50 +0200181 }
182
Tomáš Peckabd43a942021-08-04 10:12:49 +0200183 SECTION("Enabled IPv6 protocol must have at least one IP or the autoconfiguration must be on")
Tomáš Peckade0ef2f2021-07-08 13:23:50 +0200184 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100185 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/enabled", "true");
186 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:enabled", "false");
187 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:enabled", "true");
188 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:autoconf/ietf-ip:create-global-addresses", "false");
189 REQUIRE_THROWS_AS(client.applyChanges(), sysrepo::ErrorWithCode);
Tomáš Peckade0ef2f2021-07-08 13:23:50 +0200190 }
191 }
Tomáš Pecka39bbb512021-05-23 22:07:22 +0200192
193 std::string expectedContents;
Tomáš Peckabd43a942021-08-04 10:12:49 +0200194
Tomáš Pecka85f77752021-06-03 10:09:58 +0200195 SECTION("Setting IPs to eth0")
Tomáš Pecka39bbb512021-05-23 22:07:22 +0200196 {
Tomáš Pecka85f77752021-06-03 10:09:58 +0200197 const auto expectedFilePath = fakeConfigDir / "eth0.network";
198
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100199 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/type", "iana-if-type:ethernetCsmacd");
Tomáš Pecka85f77752021-06-03 10:09:58 +0200200
201 SECTION("Single IPv4 address")
202 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100203 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/description", "Hello world");
204 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24");
205 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "false");
Tomáš Pecka85f77752021-06-03 10:09:58 +0200206 expectedContents = R"([Match]
Tomáš Pecka39bbb512021-05-23 22:07:22 +0200207Name=eth0
208
209[Network]
210Description=Hello world
Tomáš Pecka85f77752021-06-03 10:09:58 +0200211Address=192.0.2.1/24
212LinkLocalAddressing=no
Tomáš Peckabd43a942021-08-04 10:12:49 +0200213IPv6AcceptRA=false
Tomáš Pecka85f77752021-06-03 10:09:58 +0200214DHCP=no
Tomáš Pecka39bbb512021-05-23 22:07:22 +0200215LLDP=true
216EmitLLDP=nearest-bridge
217)";
Tomáš Pecka85f77752021-06-03 10:09:58 +0200218 }
Tomáš Pecka39bbb512021-05-23 22:07:22 +0200219
Tomáš Pecka85f77752021-06-03 10:09:58 +0200220 SECTION("Two IPv4 addresses")
221 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100222 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24");
223 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.2']/ietf-ip:prefix-length", "24");
224 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "false");
225 client.deleteItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6");
Tomáš Pecka85f77752021-06-03 10:09:58 +0200226 expectedContents = R"([Match]
Tomáš Pecka39bbb512021-05-23 22:07:22 +0200227Name=eth0
228
229[Network]
Tomáš Pecka85f77752021-06-03 10:09:58 +0200230Address=192.0.2.1/24
231Address=192.0.2.2/24
232LinkLocalAddressing=no
Tomáš Peckabd43a942021-08-04 10:12:49 +0200233IPv6AcceptRA=false
Tomáš Pecka85f77752021-06-03 10:09:58 +0200234DHCP=no
Tomáš Pecka39bbb512021-05-23 22:07:22 +0200235LLDP=true
236EmitLLDP=nearest-bridge
237)";
Tomáš Pecka85f77752021-06-03 10:09:58 +0200238 }
239
240 SECTION("IPv4 and IPv6 addresses")
241 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100242 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24");
243 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:address[ip='2001:db8::1']/ietf-ip:prefix-length", "32");
244 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "false");
Tomáš Pecka85f77752021-06-03 10:09:58 +0200245 expectedContents = R"([Match]
246Name=eth0
247
248[Network]
249Address=192.0.2.1/24
250Address=2001:db8::1/32
Tomáš Peckabd43a942021-08-04 10:12:49 +0200251IPv6AcceptRA=true
Tomáš Pecka85f77752021-06-03 10:09:58 +0200252DHCP=no
253LLDP=true
254EmitLLDP=nearest-bridge
255)";
256 }
257
258 SECTION("IPv4 and IPv6 addresses but IPv6 disabled")
259 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100260 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24");
261 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:address[ip='2001:db8::1']/ietf-ip:prefix-length", "32");
262 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "false");
263 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/enabled", "false");
Tomáš Pecka85f77752021-06-03 10:09:58 +0200264 expectedContents = R"([Match]
265Name=eth0
266
267[Network]
268Address=192.0.2.1/24
269LinkLocalAddressing=no
Tomáš Peckabd43a942021-08-04 10:12:49 +0200270IPv6AcceptRA=false
Tomáš Pecka85f77752021-06-03 10:09:58 +0200271DHCP=no
272LLDP=true
273EmitLLDP=nearest-bridge
274)";
275 }
276
277 REQUIRE_CALL(fake, cb(std::vector<std::string>{"eth0"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100278 client.applyChanges();
Tomáš Pecka85f77752021-06-03 10:09:58 +0200279 REQUIRE(std::filesystem::exists(expectedFilePath));
280 REQUIRE(velia::utils::readFileToString(expectedFilePath) == expectedContents);
281
282 // reset the contents
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100283 client.deleteItem("/ietf-interfaces:interfaces/interface[name='eth0']");
Tomáš Pecka85f77752021-06-03 10:09:58 +0200284 REQUIRE_CALL(fake, cb(std::vector<std::string>{"eth0"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100285 client.applyChanges();
Tomáš Pecka85f77752021-06-03 10:09:58 +0200286 REQUIRE(!std::filesystem::exists(expectedFilePath));
Tomáš Pecka39bbb512021-05-23 22:07:22 +0200287 }
288
Tomáš Peckac2929a62021-06-05 17:21:03 +0200289 SECTION("Two links")
Tomáš Pecka85f77752021-06-03 10:09:58 +0200290 {
291 const auto expectedFilePathEth0 = fakeConfigDir / "eth0.network";
292 const auto expectedFilePathEth1 = fakeConfigDir / "eth1.network";
Tomáš Peckac2929a62021-06-05 17:21:03 +0200293
294 std::string expectedContentsEth0 = R"([Match]
Tomáš Pecka85f77752021-06-03 10:09:58 +0200295Name=eth0
Tomáš Pecka39bbb512021-05-23 22:07:22 +0200296
Tomáš Pecka85f77752021-06-03 10:09:58 +0200297[Network]
298Address=192.0.2.1/24
299LinkLocalAddressing=no
Tomáš Peckabd43a942021-08-04 10:12:49 +0200300IPv6AcceptRA=false
Tomáš Pecka85f77752021-06-03 10:09:58 +0200301DHCP=no
302LLDP=true
303EmitLLDP=nearest-bridge
304)";
Tomáš Peckac2929a62021-06-05 17:21:03 +0200305 std::string expectedContentsEth1 = R"([Match]
Tomáš Pecka85f77752021-06-03 10:09:58 +0200306Name=eth1
Tomáš Pecka39bbb512021-05-23 22:07:22 +0200307
Tomáš Pecka85f77752021-06-03 10:09:58 +0200308[Network]
309Address=2001:db8::1/32
Tomáš Peckabd43a942021-08-04 10:12:49 +0200310IPv6AcceptRA=true
Tomáš Pecka85f77752021-06-03 10:09:58 +0200311DHCP=no
312LLDP=true
313EmitLLDP=nearest-bridge
314)";
315
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100316 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/type", "iana-if-type:ethernetCsmacd");
317 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24");
318 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "false");
319 client.setItem("/ietf-interfaces:interfaces/interface[name='eth1']/type", "iana-if-type:ethernetCsmacd");
320 client.setItem("/ietf-interfaces:interfaces/interface[name='eth1']/ietf-ip:ipv6/ietf-ip:address[ip='2001:db8::1']/ietf-ip:prefix-length", "32");
Tomáš Pecka85f77752021-06-03 10:09:58 +0200321
322 REQUIRE_CALL(fake, cb(std::vector<std::string>{"eth0", "eth1"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100323 client.applyChanges();
Tomáš Pecka85f77752021-06-03 10:09:58 +0200324 REQUIRE(std::filesystem::exists(expectedFilePathEth0));
325 REQUIRE(std::filesystem::exists(expectedFilePathEth1));
326 REQUIRE(velia::utils::readFileToString(expectedFilePathEth0) == expectedContentsEth0);
327 REQUIRE(velia::utils::readFileToString(expectedFilePathEth1) == expectedContentsEth1);
328
329 // reset the contents
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100330 client.deleteItem("/ietf-interfaces:interfaces/interface[name='eth0']");
331 client.deleteItem("/ietf-interfaces:interfaces/interface[name='eth1']");
Tomáš Pecka85f77752021-06-03 10:09:58 +0200332 REQUIRE_CALL(fake, cb(std::vector<std::string>{"eth0", "eth1"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100333 client.applyChanges();
Tomáš Pecka85f77752021-06-03 10:09:58 +0200334 REQUIRE(!std::filesystem::exists(expectedFilePathEth0));
335 REQUIRE(!std::filesystem::exists(expectedFilePathEth1));
336 }
Tomáš Peckac2929a62021-06-05 17:21:03 +0200337
338 SECTION("Setup a bridge br0 over eth0 and eth1")
339 {
340 const auto expectedFilePathBr0 = fakeConfigDir / "br0.network";
341 const auto expectedFilePathEth0 = fakeConfigDir / "eth0.network";
342 const auto expectedFilePathEth1 = fakeConfigDir / "eth1.network";
343
344 std::string expectedContentsBr0 = R"([Match]
345Name=br0
346
347[Network]
348LinkLocalAddressing=no
Tomáš Peckabd43a942021-08-04 10:12:49 +0200349IPv6AcceptRA=false
Tomáš Peckac2929a62021-06-05 17:21:03 +0200350DHCP=no
351LLDP=true
352EmitLLDP=nearest-bridge
353)";
354
355 std::string expectedContentsEth0 = R"([Match]
356Name=eth0
357
358[Network]
359Bridge=br0
Tomáš Peckabd43a942021-08-04 10:12:49 +0200360IPv6AcceptRA=false
Tomáš Peckac2929a62021-06-05 17:21:03 +0200361DHCP=no
362LLDP=true
363EmitLLDP=nearest-bridge
364)";
365
366 std::string expectedContentsEth1 = R"([Match]
367Name=eth1
368
369[Network]
370Bridge=br0
Tomáš Peckabd43a942021-08-04 10:12:49 +0200371IPv6AcceptRA=false
Tomáš Peckac2929a62021-06-05 17:21:03 +0200372DHCP=no
373LLDP=true
374EmitLLDP=nearest-bridge
375)";
376
377 // create br0 bridge over eth0 and eth1 with no IP
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100378 client.setItem("/ietf-interfaces:interfaces/interface[name='br0']/enabled", "false");
379 client.setItem("/ietf-interfaces:interfaces/interface[name='br0']/type", "iana-if-type:bridge");
Tomáš Peckac2929a62021-06-05 17:21:03 +0200380
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100381 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/type", "iana-if-type:ethernetCsmacd");
382 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/czechlight-network:bridge", "br0");
383 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:enabled", "false");
384 client.deleteItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4");
Tomáš Peckac2929a62021-06-05 17:21:03 +0200385
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100386 client.setItem("/ietf-interfaces:interfaces/interface[name='eth1']/type", "iana-if-type:ethernetCsmacd");
387 client.setItem("/ietf-interfaces:interfaces/interface[name='eth1']/czechlight-network:bridge", "br0");
388 client.setItem("/ietf-interfaces:interfaces/interface[name='eth1']/ietf-ip:ipv4/ietf-ip:enabled", "false");
389 client.deleteItem("/ietf-interfaces:interfaces/interface[name='eth1']/ietf-ip:ipv6");
Tomáš Peckac2929a62021-06-05 17:21:03 +0200390
391 REQUIRE_CALL(fake, cb(std::vector<std::string>{"br0", "eth0", "eth1"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100392 client.applyChanges();
Tomáš Peckac2929a62021-06-05 17:21:03 +0200393 REQUIRE(std::filesystem::exists(expectedFilePathBr0));
394 REQUIRE(std::filesystem::exists(expectedFilePathEth0));
395 REQUIRE(std::filesystem::exists(expectedFilePathEth1));
396 REQUIRE(velia::utils::readFileToString(expectedFilePathBr0) == expectedContentsBr0);
397 REQUIRE(velia::utils::readFileToString(expectedFilePathEth0) == expectedContentsEth0);
398 REQUIRE(velia::utils::readFileToString(expectedFilePathEth1) == expectedContentsEth1);
399
400 // assign an IPv4 address to br0
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100401 client.setItem("/ietf-interfaces:interfaces/interface[name='br0']/enabled", "true");
402 client.setItem("/ietf-interfaces:interfaces/interface[name='br0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24");
403 client.setItem("/ietf-interfaces:interfaces/interface[name='br0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "false");
Tomáš Peckac2929a62021-06-05 17:21:03 +0200404 expectedContentsBr0 = R"([Match]
405Name=br0
406
407[Network]
408Address=192.0.2.1/24
409LinkLocalAddressing=no
Tomáš Peckabd43a942021-08-04 10:12:49 +0200410IPv6AcceptRA=false
Tomáš Peckac2929a62021-06-05 17:21:03 +0200411DHCP=no
412LLDP=true
413EmitLLDP=nearest-bridge
414)";
415
416 REQUIRE_CALL(fake, cb(std::vector<std::string>{"br0"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100417 client.applyChanges();
Tomáš Peckac2929a62021-06-05 17:21:03 +0200418 REQUIRE(std::filesystem::exists(expectedFilePathBr0));
419 REQUIRE(std::filesystem::exists(expectedFilePathEth0));
420 REQUIRE(std::filesystem::exists(expectedFilePathEth1));
421 REQUIRE(velia::utils::readFileToString(expectedFilePathBr0) == expectedContentsBr0);
422 REQUIRE(velia::utils::readFileToString(expectedFilePathEth0) == expectedContentsEth0);
423 REQUIRE(velia::utils::readFileToString(expectedFilePathEth1) == expectedContentsEth1);
424
425 // assign also an IPv6 address to br0
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100426 client.setItem("/ietf-interfaces:interfaces/interface[name='br0']/ietf-ip:ipv6/ietf-ip:address[ip='2001:db8::1']/ietf-ip:prefix-length", "32");
Tomáš Peckac2929a62021-06-05 17:21:03 +0200427 expectedContentsBr0 = R"([Match]
428Name=br0
429
430[Network]
431Address=192.0.2.1/24
432Address=2001:db8::1/32
Tomáš Peckabd43a942021-08-04 10:12:49 +0200433IPv6AcceptRA=true
Tomáš Peckac2929a62021-06-05 17:21:03 +0200434DHCP=no
435LLDP=true
436EmitLLDP=nearest-bridge
437)";
438
439 REQUIRE_CALL(fake, cb(std::vector<std::string>{"br0"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100440 client.applyChanges();
Tomáš Peckac2929a62021-06-05 17:21:03 +0200441 REQUIRE(std::filesystem::exists(expectedFilePathBr0));
442 REQUIRE(std::filesystem::exists(expectedFilePathEth0));
443 REQUIRE(std::filesystem::exists(expectedFilePathEth1));
444 REQUIRE(velia::utils::readFileToString(expectedFilePathBr0) == expectedContentsBr0);
445 REQUIRE(velia::utils::readFileToString(expectedFilePathEth0) == expectedContentsEth0);
446 REQUIRE(velia::utils::readFileToString(expectedFilePathEth1) == expectedContentsEth1);
447
448 // remove eth1 from bridge
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100449 client.deleteItem("/ietf-interfaces:interfaces/interface[name='eth1']/czechlight-network:bridge");
450 client.setItem("/ietf-interfaces:interfaces/interface[name='eth1']/ietf-ip:ipv6/ietf-ip:address[ip='2001:db8::2']/ietf-ip:prefix-length", "32");
Tomáš Peckac2929a62021-06-05 17:21:03 +0200451
452 expectedContentsEth1 = R"([Match]
453Name=eth1
454
455[Network]
456Address=2001:db8::2/32
Tomáš Peckabd43a942021-08-04 10:12:49 +0200457IPv6AcceptRA=true
Tomáš Peckac2929a62021-06-05 17:21:03 +0200458DHCP=no
459LLDP=true
460EmitLLDP=nearest-bridge
461)";
462
463 REQUIRE_CALL(fake, cb(std::vector<std::string>{"eth1"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100464 client.applyChanges();
Tomáš Peckac2929a62021-06-05 17:21:03 +0200465 REQUIRE(std::filesystem::exists(expectedFilePathBr0));
466 REQUIRE(std::filesystem::exists(expectedFilePathEth0));
467 REQUIRE(std::filesystem::exists(expectedFilePathEth1));
468 REQUIRE(velia::utils::readFileToString(expectedFilePathBr0) == expectedContentsBr0);
469 REQUIRE(velia::utils::readFileToString(expectedFilePathEth0) == expectedContentsEth0);
470 REQUIRE(velia::utils::readFileToString(expectedFilePathEth1) == expectedContentsEth1);
471
472 // reset the contents
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100473 client.deleteItem("/ietf-interfaces:interfaces/interface[name='br0']");
474 client.deleteItem("/ietf-interfaces:interfaces/interface[name='eth0']");
475 client.deleteItem("/ietf-interfaces:interfaces/interface[name='eth1']");
Tomáš Peckac2929a62021-06-05 17:21:03 +0200476 REQUIRE_CALL(fake, cb(std::vector<std::string>{"br0", "eth0", "eth1"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100477 client.applyChanges();
Tomáš Peckac2929a62021-06-05 17:21:03 +0200478 REQUIRE(!std::filesystem::exists(expectedFilePathBr0));
479 REQUIRE(!std::filesystem::exists(expectedFilePathEth0));
480 REQUIRE(!std::filesystem::exists(expectedFilePathEth1));
481 }
482
483 SECTION("Slave interface and enabled/disabled IP protocols")
484 {
485 const auto expectedFilePathBr0 = fakeConfigDir / "br0.network";
486 const auto expectedFilePathEth0 = fakeConfigDir / "eth0.network";
487
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100488 client.setItem("/ietf-interfaces:interfaces/interface[name='br0']/type", "iana-if-type:bridge");
489 client.setItem("/ietf-interfaces:interfaces/interface[name='br0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24");
490 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/type", "iana-if-type:ethernetCsmacd");
491 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/czechlight-network:bridge", "br0");
Tomáš Peckac2929a62021-06-05 17:21:03 +0200492
493 SECTION("Can't be a slave when IPv4 enabled")
494 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100495 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24");
496 REQUIRE_THROWS_AS(client.applyChanges(), sysrepo::ErrorWithCode);
Tomáš Peckac2929a62021-06-05 17:21:03 +0200497 }
498
499 SECTION("Can't be a slave when IPv6 enabled")
500 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100501 client.setItem("/ietf-interfaces:interfaces/interface[name='eth1']/ietf-ip:ipv6/ietf-ip:address[ip='2001:db8::1']/ietf-ip:prefix-length", "32");
502 REQUIRE_THROWS_AS(client.applyChanges(), sysrepo::ErrorWithCode);
Tomáš Peckac2929a62021-06-05 17:21:03 +0200503 }
504
505 SECTION("Can't be a slave when both IPv4 and IPv6 enabled")
506 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100507 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24");
508 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:address[ip='2001:db8::1']/ietf-ip:prefix-length", "32");
509 REQUIRE_THROWS_AS(client.applyChanges(), sysrepo::ErrorWithCode);
Tomáš Peckac2929a62021-06-05 17:21:03 +0200510 }
511
512 SECTION("Can be a slave when addresses present but protocol is disabled")
513 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100514 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24");
515 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/enabled", "false");
Tomáš Peckac2929a62021-06-05 17:21:03 +0200516
517 REQUIRE_CALL(fake, cb(std::vector<std::string>{"br0", "eth0"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100518 client.applyChanges();
Tomáš Peckac2929a62021-06-05 17:21:03 +0200519
520 // reset the contents
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100521 client.deleteItem("/ietf-interfaces:interfaces/interface[name='br0']");
522 client.deleteItem("/ietf-interfaces:interfaces/interface[name='eth0']");
Tomáš Peckac2929a62021-06-05 17:21:03 +0200523 REQUIRE_CALL(fake, cb(std::vector<std::string>{"br0", "eth0"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100524 client.applyChanges();
Tomáš Peckac2929a62021-06-05 17:21:03 +0200525 REQUIRE(!std::filesystem::exists(expectedFilePathBr0));
526 REQUIRE(!std::filesystem::exists(expectedFilePathEth0));
527 }
528 }
Tomáš Peckabd43a942021-08-04 10:12:49 +0200529
530 SECTION("Network autoconfiguration")
531 {
532 const auto expectedFilePath = fakeConfigDir / "eth0.network";
533
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100534 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/type", "iana-if-type:ethernetCsmacd");
Tomáš Peckabd43a942021-08-04 10:12:49 +0200535
536 SECTION("IPv4 on with address, IPv6 disabled, DHCPv4 off, RA off")
537 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100538 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "false");
539 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24"); // in case DHCP is disabled an IP must be present
Tomáš Peckabd43a942021-08-04 10:12:49 +0200540
541 expectedContents = R"([Match]
542Name=eth0
543
544[Network]
545Address=192.0.2.1/24
546LinkLocalAddressing=no
547IPv6AcceptRA=false
548DHCP=no
549LLDP=true
550EmitLLDP=nearest-bridge
551)";
552 }
553
554 SECTION("IPv4 on with address, IPv6 disabled, DHCPv4 on, RA on")
555 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100556 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "true");
557 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24"); // in case DHCP is disabled an IP must be present
558 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:enabled", "false");
559 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:autoconf/ietf-ip:create-global-addresses", "true");
Tomáš Peckabd43a942021-08-04 10:12:49 +0200560
561 expectedContents = R"([Match]
562Name=eth0
563
564[Network]
565Address=192.0.2.1/24
566LinkLocalAddressing=no
567IPv6AcceptRA=false
568DHCP=ipv4
569LLDP=true
570EmitLLDP=nearest-bridge
571)";
572 }
573
574 SECTION("IPv4 disabled, IPv6 enabled, DHCPv4 on, RA on")
575 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100576 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:enabled", "false");
577 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "true");
578 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:enabled", "true");
579 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:autoconf/ietf-ip:create-global-addresses", "true");
Tomáš Peckabd43a942021-08-04 10:12:49 +0200580
581 expectedContents = R"([Match]
582Name=eth0
583
584[Network]
585IPv6AcceptRA=true
586DHCP=no
587LLDP=true
588EmitLLDP=nearest-bridge
589)";
590 }
591
592 SECTION("IPv4 enabled, IPv6 enabled, DHCPv4 on, RA on")
593 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100594 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:enabled", "true");
595 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "true");
596 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:enabled", "true");
597 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:autoconf/ietf-ip:create-global-addresses", "true");
Tomáš Peckabd43a942021-08-04 10:12:49 +0200598
599 expectedContents = R"([Match]
600Name=eth0
601
602[Network]
603IPv6AcceptRA=true
604DHCP=ipv4
605LLDP=true
606EmitLLDP=nearest-bridge
607)";
608 }
609
610 SECTION("IPv4 enabled, IPv6 enabled, DHCPv4 off, RA on")
611 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100612 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:enabled", "true");
613 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/prefix-length", "24");
614 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "false");
615 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:enabled", "true");
616 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:autoconf/ietf-ip:create-global-addresses", "true");
Tomáš Peckabd43a942021-08-04 10:12:49 +0200617
618 expectedContents = R"([Match]
619Name=eth0
620
621[Network]
622Address=192.0.2.1/24
623IPv6AcceptRA=true
624DHCP=no
625LLDP=true
626EmitLLDP=nearest-bridge
627)";
628 }
629
630 SECTION("IPv4 disabled, IPv6 disabled, DHCPv4 off, RA off")
631 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100632 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/prefix-length", "24");
633 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "false");
634 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:address[ip='2001:db8::1']/prefix-length", "32");
635 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:autoconf/ietf-ip:create-global-addresses", "false");
Tomáš Peckabd43a942021-08-04 10:12:49 +0200636
637 expectedContents = R"([Match]
638Name=eth0
639
640[Network]
641Address=192.0.2.1/24
642Address=2001:db8::1/32
643IPv6AcceptRA=false
644DHCP=no
645LLDP=true
646EmitLLDP=nearest-bridge
647)";
648 }
649
650 REQUIRE_CALL(fake, cb(std::vector<std::string>{"eth0"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100651 client.applyChanges();
Tomáš Peckabd43a942021-08-04 10:12:49 +0200652 REQUIRE(std::filesystem::exists(expectedFilePath));
653 REQUIRE(velia::utils::readFileToString(expectedFilePath) == expectedContents);
654
655 // reset the contents
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100656 client.deleteItem("/ietf-interfaces:interfaces/interface[name='eth0']");
Tomáš Peckabd43a942021-08-04 10:12:49 +0200657 REQUIRE_CALL(fake, cb(std::vector<std::string>{"eth0"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100658 client.applyChanges();
Tomáš Peckabd43a942021-08-04 10:12:49 +0200659 REQUIRE(!std::filesystem::exists(expectedFilePath));
660 }
Tomáš Pecka39bbb512021-05-23 22:07:22 +0200661}