blob: e20503e7ea0cf5887fb3854af48745fc35efa921 [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
72 auto fake = FakeNetworkReload();
73
74 const auto fakeConfigDir = std::filesystem::path(CMAKE_CURRENT_BINARY_DIR) / "tests/network/"s;
75 std::filesystem::remove_all(fakeConfigDir);
76 std::filesystem::create_directories(fakeConfigDir);
77
78 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 +020079
80 SECTION("Link changes disabled")
81 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +010082 client.switchDatastore(sysrepo::Datastore::Startup);
Tomáš Pecka71a98b02021-05-17 15:59:40 +020083
84 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"}}) {
Václav Kubernát7efd6d52021-11-09 01:31:11 +010092 client.setItem(("/ietf-interfaces:interfaces/interface[name='"s + name + "']/type").c_str(), type);
93 client.setItem(("/ietf-interfaces:interfaces/interface[name='"s + name + "']/enabled").c_str(), "false");
Tomáš Pecka71a98b02021-05-17 15:59:40 +020094 }
Václav Kubernát7efd6d52021-11-09 01:31:11 +010095 client.applyChanges();
Tomáš Pecka71a98b02021-05-17 15:59:40 +020096 }
97
98 SECTION("Invalid type for a valid link")
99 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100100 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/type", "iana-if-type:softwareLoopback");
101 REQUIRE_THROWS_AS(client.applyChanges(), sysrepo::ErrorWithCode);
Tomáš Pecka71a98b02021-05-17 15:59:40 +0200102 }
103
104 SECTION("Invalid name")
105 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100106 client.setItem("/ietf-interfaces:interfaces/interface[name='blah0']/type", "iana-if-type:ethernetCsmacd");
107 REQUIRE_THROWS_AS(client.applyChanges(), sysrepo::ErrorWithCode);
Tomáš Pecka71a98b02021-05-17 15:59:40 +0200108 }
109 }
Tomáš Peckade0ef2f2021-07-08 13:23:50 +0200110
111 SECTION("There must always be enabled protocol or the interface must be explicitely disabled")
112 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100113 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/type", "iana-if-type:ethernetCsmacd");
Tomáš Peckade0ef2f2021-07-08 13:23:50 +0200114
115 SECTION("Disabled protocols; enabled link")
116 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100117 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/enabled", "true");
118 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:enabled", "false");
119 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:enabled", "false");
120 REQUIRE_THROWS_AS(client.applyChanges(), sysrepo::ErrorWithCode);
Tomáš Peckade0ef2f2021-07-08 13:23:50 +0200121 }
122
123 SECTION("Active protocols; disabled link")
124 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100125 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/enabled", "false");
126 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:enabled", "false");
127 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:enabled", "true");
128 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 +0200129 REQUIRE_CALL(fake, cb(std::vector<std::string>{"eth0"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100130 client.applyChanges();
Tomáš Peckade0ef2f2021-07-08 13:23:50 +0200131 }
132
133 SECTION("IPv4 only; enabled link")
134 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100135 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/enabled", "true");
136 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:enabled", "true");
137 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24");
138 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:enabled", "false");
Tomáš Pecka4c7ff2a2021-08-06 15:05:54 +0200139 REQUIRE_CALL(fake, cb(std::vector<std::string>{"eth0"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100140 client.applyChanges();
Tomáš Peckade0ef2f2021-07-08 13:23:50 +0200141 }
142 }
143
144 SECTION("Every active protocol must have at least one IP address assigned")
145 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100146 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/enabled", "false");
147 client.deleteItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4");
148 client.deleteItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6");
Tomáš Pecka4c7ff2a2021-08-06 15:05:54 +0200149 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 +0100150 client.applyChanges();
Tomáš Peckade0ef2f2021-07-08 13:23:50 +0200151
152 SECTION("Enabled IPv4 protocol with some IPs assigned is valid")
153 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100154 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/enabled", "true");
155 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:enabled", "true");
156 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24");
157 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.2']/ietf-ip:prefix-length", "24");
158 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:enabled", "false");
Tomáš Pecka4c7ff2a2021-08-06 15:05:54 +0200159 REQUIRE_CALL(fake, cb(std::vector<std::string>{"eth0"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100160 client.applyChanges();
Tomáš Peckade0ef2f2021-07-08 13:23:50 +0200161 }
162
163 SECTION("Enabled IPv6 protocol with some IPs assigned is valid")
164 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100165 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/enabled", "true");
166 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:enabled", "false");
167 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:enabled", "true");
168 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 +0200169 REQUIRE_CALL(fake, cb(std::vector<std::string>{"eth0"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100170 client.applyChanges();
Tomáš Peckade0ef2f2021-07-08 13:23:50 +0200171 }
172
Tomáš Peckabd43a942021-08-04 10:12:49 +0200173 SECTION("Enabled IPv4 protocol must have at least one IP or the autoconfiguration must be on")
Tomáš Peckade0ef2f2021-07-08 13:23:50 +0200174 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100175 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/enabled", "true");
176 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:enabled", "true");
177 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "false");
178 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:enabled", "false");
179 REQUIRE_THROWS_AS(client.applyChanges(), sysrepo::ErrorWithCode);
Tomáš Peckade0ef2f2021-07-08 13:23:50 +0200180 }
181
Tomáš Peckabd43a942021-08-04 10:12:49 +0200182 SECTION("Enabled IPv6 protocol must have at least one IP or the autoconfiguration must be on")
Tomáš Peckade0ef2f2021-07-08 13:23:50 +0200183 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100184 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/enabled", "true");
185 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:enabled", "false");
186 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:enabled", "true");
187 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:autoconf/ietf-ip:create-global-addresses", "false");
188 REQUIRE_THROWS_AS(client.applyChanges(), sysrepo::ErrorWithCode);
Tomáš Peckade0ef2f2021-07-08 13:23:50 +0200189 }
190 }
Tomáš Pecka39bbb512021-05-23 22:07:22 +0200191
192 std::string expectedContents;
Tomáš Peckabd43a942021-08-04 10:12:49 +0200193
Tomáš Pecka85f77752021-06-03 10:09:58 +0200194 SECTION("Setting IPs to eth0")
Tomáš Pecka39bbb512021-05-23 22:07:22 +0200195 {
Tomáš Pecka85f77752021-06-03 10:09:58 +0200196 const auto expectedFilePath = fakeConfigDir / "eth0.network";
197
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100198 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/type", "iana-if-type:ethernetCsmacd");
Tomáš Pecka85f77752021-06-03 10:09:58 +0200199
200 SECTION("Single IPv4 address")
201 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100202 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/description", "Hello world");
203 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24");
204 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "false");
Tomáš Pecka85f77752021-06-03 10:09:58 +0200205 expectedContents = R"([Match]
Tomáš Pecka39bbb512021-05-23 22:07:22 +0200206Name=eth0
207
208[Network]
209Description=Hello world
Tomáš Pecka85f77752021-06-03 10:09:58 +0200210Address=192.0.2.1/24
211LinkLocalAddressing=no
Tomáš Peckabd43a942021-08-04 10:12:49 +0200212IPv6AcceptRA=false
Tomáš Pecka85f77752021-06-03 10:09:58 +0200213DHCP=no
Tomáš Pecka39bbb512021-05-23 22:07:22 +0200214LLDP=true
215EmitLLDP=nearest-bridge
216)";
Tomáš Pecka85f77752021-06-03 10:09:58 +0200217 }
Tomáš Pecka39bbb512021-05-23 22:07:22 +0200218
Tomáš Pecka85f77752021-06-03 10:09:58 +0200219 SECTION("Two IPv4 addresses")
220 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100221 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24");
222 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.2']/ietf-ip:prefix-length", "24");
223 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "false");
224 client.deleteItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6");
Tomáš Pecka85f77752021-06-03 10:09:58 +0200225 expectedContents = R"([Match]
Tomáš Pecka39bbb512021-05-23 22:07:22 +0200226Name=eth0
227
228[Network]
Tomáš Pecka85f77752021-06-03 10:09:58 +0200229Address=192.0.2.1/24
230Address=192.0.2.2/24
231LinkLocalAddressing=no
Tomáš Peckabd43a942021-08-04 10:12:49 +0200232IPv6AcceptRA=false
Tomáš Pecka85f77752021-06-03 10:09:58 +0200233DHCP=no
Tomáš Pecka39bbb512021-05-23 22:07:22 +0200234LLDP=true
235EmitLLDP=nearest-bridge
236)";
Tomáš Pecka85f77752021-06-03 10:09:58 +0200237 }
238
239 SECTION("IPv4 and IPv6 addresses")
240 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100241 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24");
242 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:address[ip='2001:db8::1']/ietf-ip:prefix-length", "32");
243 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "false");
Tomáš Pecka85f77752021-06-03 10:09:58 +0200244 expectedContents = R"([Match]
245Name=eth0
246
247[Network]
248Address=192.0.2.1/24
249Address=2001:db8::1/32
Tomáš Peckabd43a942021-08-04 10:12:49 +0200250IPv6AcceptRA=true
Tomáš Pecka85f77752021-06-03 10:09:58 +0200251DHCP=no
252LLDP=true
253EmitLLDP=nearest-bridge
254)";
255 }
256
257 SECTION("IPv4 and IPv6 addresses but IPv6 disabled")
258 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100259 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24");
260 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:address[ip='2001:db8::1']/ietf-ip:prefix-length", "32");
261 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "false");
262 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/enabled", "false");
Tomáš Pecka85f77752021-06-03 10:09:58 +0200263 expectedContents = R"([Match]
264Name=eth0
265
266[Network]
267Address=192.0.2.1/24
268LinkLocalAddressing=no
Tomáš Peckabd43a942021-08-04 10:12:49 +0200269IPv6AcceptRA=false
Tomáš Pecka85f77752021-06-03 10:09:58 +0200270DHCP=no
271LLDP=true
272EmitLLDP=nearest-bridge
273)";
274 }
275
276 REQUIRE_CALL(fake, cb(std::vector<std::string>{"eth0"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100277 client.applyChanges();
Tomáš Pecka85f77752021-06-03 10:09:58 +0200278 REQUIRE(std::filesystem::exists(expectedFilePath));
279 REQUIRE(velia::utils::readFileToString(expectedFilePath) == expectedContents);
280
281 // reset the contents
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100282 client.deleteItem("/ietf-interfaces:interfaces/interface[name='eth0']");
Tomáš Pecka85f77752021-06-03 10:09:58 +0200283 REQUIRE_CALL(fake, cb(std::vector<std::string>{"eth0"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100284 client.applyChanges();
Tomáš Pecka85f77752021-06-03 10:09:58 +0200285 REQUIRE(!std::filesystem::exists(expectedFilePath));
Tomáš Pecka39bbb512021-05-23 22:07:22 +0200286 }
287
Tomáš Peckac2929a62021-06-05 17:21:03 +0200288 SECTION("Two links")
Tomáš Pecka85f77752021-06-03 10:09:58 +0200289 {
290 const auto expectedFilePathEth0 = fakeConfigDir / "eth0.network";
291 const auto expectedFilePathEth1 = fakeConfigDir / "eth1.network";
Tomáš Peckac2929a62021-06-05 17:21:03 +0200292
293 std::string expectedContentsEth0 = R"([Match]
Tomáš Pecka85f77752021-06-03 10:09:58 +0200294Name=eth0
Tomáš Pecka39bbb512021-05-23 22:07:22 +0200295
Tomáš Pecka85f77752021-06-03 10:09:58 +0200296[Network]
297Address=192.0.2.1/24
298LinkLocalAddressing=no
Tomáš Peckabd43a942021-08-04 10:12:49 +0200299IPv6AcceptRA=false
Tomáš Pecka85f77752021-06-03 10:09:58 +0200300DHCP=no
301LLDP=true
302EmitLLDP=nearest-bridge
303)";
Tomáš Peckac2929a62021-06-05 17:21:03 +0200304 std::string expectedContentsEth1 = R"([Match]
Tomáš Pecka85f77752021-06-03 10:09:58 +0200305Name=eth1
Tomáš Pecka39bbb512021-05-23 22:07:22 +0200306
Tomáš Pecka85f77752021-06-03 10:09:58 +0200307[Network]
308Address=2001:db8::1/32
Tomáš Peckabd43a942021-08-04 10:12:49 +0200309IPv6AcceptRA=true
Tomáš Pecka85f77752021-06-03 10:09:58 +0200310DHCP=no
311LLDP=true
312EmitLLDP=nearest-bridge
313)";
314
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100315 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/type", "iana-if-type:ethernetCsmacd");
316 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24");
317 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "false");
318 client.setItem("/ietf-interfaces:interfaces/interface[name='eth1']/type", "iana-if-type:ethernetCsmacd");
319 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 +0200320
321 REQUIRE_CALL(fake, cb(std::vector<std::string>{"eth0", "eth1"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100322 client.applyChanges();
Tomáš Pecka85f77752021-06-03 10:09:58 +0200323 REQUIRE(std::filesystem::exists(expectedFilePathEth0));
324 REQUIRE(std::filesystem::exists(expectedFilePathEth1));
325 REQUIRE(velia::utils::readFileToString(expectedFilePathEth0) == expectedContentsEth0);
326 REQUIRE(velia::utils::readFileToString(expectedFilePathEth1) == expectedContentsEth1);
327
328 // reset the contents
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100329 client.deleteItem("/ietf-interfaces:interfaces/interface[name='eth0']");
330 client.deleteItem("/ietf-interfaces:interfaces/interface[name='eth1']");
Tomáš Pecka85f77752021-06-03 10:09:58 +0200331 REQUIRE_CALL(fake, cb(std::vector<std::string>{"eth0", "eth1"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100332 client.applyChanges();
Tomáš Pecka85f77752021-06-03 10:09:58 +0200333 REQUIRE(!std::filesystem::exists(expectedFilePathEth0));
334 REQUIRE(!std::filesystem::exists(expectedFilePathEth1));
335 }
Tomáš Peckac2929a62021-06-05 17:21:03 +0200336
337 SECTION("Setup a bridge br0 over eth0 and eth1")
338 {
339 const auto expectedFilePathBr0 = fakeConfigDir / "br0.network";
340 const auto expectedFilePathEth0 = fakeConfigDir / "eth0.network";
341 const auto expectedFilePathEth1 = fakeConfigDir / "eth1.network";
342
343 std::string expectedContentsBr0 = R"([Match]
344Name=br0
345
346[Network]
347LinkLocalAddressing=no
Tomáš Peckabd43a942021-08-04 10:12:49 +0200348IPv6AcceptRA=false
Tomáš Peckac2929a62021-06-05 17:21:03 +0200349DHCP=no
350LLDP=true
351EmitLLDP=nearest-bridge
352)";
353
354 std::string expectedContentsEth0 = R"([Match]
355Name=eth0
356
357[Network]
358Bridge=br0
Tomáš Peckabd43a942021-08-04 10:12:49 +0200359IPv6AcceptRA=false
Tomáš Peckac2929a62021-06-05 17:21:03 +0200360DHCP=no
361LLDP=true
362EmitLLDP=nearest-bridge
363)";
364
365 std::string expectedContentsEth1 = R"([Match]
366Name=eth1
367
368[Network]
369Bridge=br0
Tomáš Peckabd43a942021-08-04 10:12:49 +0200370IPv6AcceptRA=false
Tomáš Peckac2929a62021-06-05 17:21:03 +0200371DHCP=no
372LLDP=true
373EmitLLDP=nearest-bridge
374)";
375
376 // create br0 bridge over eth0 and eth1 with no IP
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100377 client.setItem("/ietf-interfaces:interfaces/interface[name='br0']/enabled", "false");
378 client.setItem("/ietf-interfaces:interfaces/interface[name='br0']/type", "iana-if-type:bridge");
Tomáš Peckac2929a62021-06-05 17:21:03 +0200379
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100380 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/type", "iana-if-type:ethernetCsmacd");
381 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/czechlight-network:bridge", "br0");
382 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:enabled", "false");
383 client.deleteItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4");
Tomáš Peckac2929a62021-06-05 17:21:03 +0200384
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100385 client.setItem("/ietf-interfaces:interfaces/interface[name='eth1']/type", "iana-if-type:ethernetCsmacd");
386 client.setItem("/ietf-interfaces:interfaces/interface[name='eth1']/czechlight-network:bridge", "br0");
387 client.setItem("/ietf-interfaces:interfaces/interface[name='eth1']/ietf-ip:ipv4/ietf-ip:enabled", "false");
388 client.deleteItem("/ietf-interfaces:interfaces/interface[name='eth1']/ietf-ip:ipv6");
Tomáš Peckac2929a62021-06-05 17:21:03 +0200389
390 REQUIRE_CALL(fake, cb(std::vector<std::string>{"br0", "eth0", "eth1"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100391 client.applyChanges();
Tomáš Peckac2929a62021-06-05 17:21:03 +0200392 REQUIRE(std::filesystem::exists(expectedFilePathBr0));
393 REQUIRE(std::filesystem::exists(expectedFilePathEth0));
394 REQUIRE(std::filesystem::exists(expectedFilePathEth1));
395 REQUIRE(velia::utils::readFileToString(expectedFilePathBr0) == expectedContentsBr0);
396 REQUIRE(velia::utils::readFileToString(expectedFilePathEth0) == expectedContentsEth0);
397 REQUIRE(velia::utils::readFileToString(expectedFilePathEth1) == expectedContentsEth1);
398
399 // assign an IPv4 address to br0
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100400 client.setItem("/ietf-interfaces:interfaces/interface[name='br0']/enabled", "true");
401 client.setItem("/ietf-interfaces:interfaces/interface[name='br0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24");
402 client.setItem("/ietf-interfaces:interfaces/interface[name='br0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "false");
Tomáš Peckac2929a62021-06-05 17:21:03 +0200403 expectedContentsBr0 = R"([Match]
404Name=br0
405
406[Network]
407Address=192.0.2.1/24
408LinkLocalAddressing=no
Tomáš Peckabd43a942021-08-04 10:12:49 +0200409IPv6AcceptRA=false
Tomáš Peckac2929a62021-06-05 17:21:03 +0200410DHCP=no
411LLDP=true
412EmitLLDP=nearest-bridge
413)";
414
415 REQUIRE_CALL(fake, cb(std::vector<std::string>{"br0"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100416 client.applyChanges();
Tomáš Peckac2929a62021-06-05 17:21:03 +0200417 REQUIRE(std::filesystem::exists(expectedFilePathBr0));
418 REQUIRE(std::filesystem::exists(expectedFilePathEth0));
419 REQUIRE(std::filesystem::exists(expectedFilePathEth1));
420 REQUIRE(velia::utils::readFileToString(expectedFilePathBr0) == expectedContentsBr0);
421 REQUIRE(velia::utils::readFileToString(expectedFilePathEth0) == expectedContentsEth0);
422 REQUIRE(velia::utils::readFileToString(expectedFilePathEth1) == expectedContentsEth1);
423
424 // assign also an IPv6 address to br0
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100425 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 +0200426 expectedContentsBr0 = R"([Match]
427Name=br0
428
429[Network]
430Address=192.0.2.1/24
431Address=2001:db8::1/32
Tomáš Peckabd43a942021-08-04 10:12:49 +0200432IPv6AcceptRA=true
Tomáš Peckac2929a62021-06-05 17:21:03 +0200433DHCP=no
434LLDP=true
435EmitLLDP=nearest-bridge
436)";
437
438 REQUIRE_CALL(fake, cb(std::vector<std::string>{"br0"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100439 client.applyChanges();
Tomáš Peckac2929a62021-06-05 17:21:03 +0200440 REQUIRE(std::filesystem::exists(expectedFilePathBr0));
441 REQUIRE(std::filesystem::exists(expectedFilePathEth0));
442 REQUIRE(std::filesystem::exists(expectedFilePathEth1));
443 REQUIRE(velia::utils::readFileToString(expectedFilePathBr0) == expectedContentsBr0);
444 REQUIRE(velia::utils::readFileToString(expectedFilePathEth0) == expectedContentsEth0);
445 REQUIRE(velia::utils::readFileToString(expectedFilePathEth1) == expectedContentsEth1);
446
447 // remove eth1 from bridge
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100448 client.deleteItem("/ietf-interfaces:interfaces/interface[name='eth1']/czechlight-network:bridge");
449 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 +0200450
451 expectedContentsEth1 = R"([Match]
452Name=eth1
453
454[Network]
455Address=2001:db8::2/32
Tomáš Peckabd43a942021-08-04 10:12:49 +0200456IPv6AcceptRA=true
Tomáš Peckac2929a62021-06-05 17:21:03 +0200457DHCP=no
458LLDP=true
459EmitLLDP=nearest-bridge
460)";
461
462 REQUIRE_CALL(fake, cb(std::vector<std::string>{"eth1"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100463 client.applyChanges();
Tomáš Peckac2929a62021-06-05 17:21:03 +0200464 REQUIRE(std::filesystem::exists(expectedFilePathBr0));
465 REQUIRE(std::filesystem::exists(expectedFilePathEth0));
466 REQUIRE(std::filesystem::exists(expectedFilePathEth1));
467 REQUIRE(velia::utils::readFileToString(expectedFilePathBr0) == expectedContentsBr0);
468 REQUIRE(velia::utils::readFileToString(expectedFilePathEth0) == expectedContentsEth0);
469 REQUIRE(velia::utils::readFileToString(expectedFilePathEth1) == expectedContentsEth1);
470
471 // reset the contents
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100472 client.deleteItem("/ietf-interfaces:interfaces/interface[name='br0']");
473 client.deleteItem("/ietf-interfaces:interfaces/interface[name='eth0']");
474 client.deleteItem("/ietf-interfaces:interfaces/interface[name='eth1']");
Tomáš Peckac2929a62021-06-05 17:21:03 +0200475 REQUIRE_CALL(fake, cb(std::vector<std::string>{"br0", "eth0", "eth1"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100476 client.applyChanges();
Tomáš Peckac2929a62021-06-05 17:21:03 +0200477 REQUIRE(!std::filesystem::exists(expectedFilePathBr0));
478 REQUIRE(!std::filesystem::exists(expectedFilePathEth0));
479 REQUIRE(!std::filesystem::exists(expectedFilePathEth1));
480 }
481
482 SECTION("Slave interface and enabled/disabled IP protocols")
483 {
484 const auto expectedFilePathBr0 = fakeConfigDir / "br0.network";
485 const auto expectedFilePathEth0 = fakeConfigDir / "eth0.network";
486
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100487 client.setItem("/ietf-interfaces:interfaces/interface[name='br0']/type", "iana-if-type:bridge");
488 client.setItem("/ietf-interfaces:interfaces/interface[name='br0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24");
489 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/type", "iana-if-type:ethernetCsmacd");
490 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/czechlight-network:bridge", "br0");
Tomáš Peckac2929a62021-06-05 17:21:03 +0200491
492 SECTION("Can't be a slave when IPv4 enabled")
493 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100494 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24");
495 REQUIRE_THROWS_AS(client.applyChanges(), sysrepo::ErrorWithCode);
Tomáš Peckac2929a62021-06-05 17:21:03 +0200496 }
497
498 SECTION("Can't be a slave when IPv6 enabled")
499 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100500 client.setItem("/ietf-interfaces:interfaces/interface[name='eth1']/ietf-ip:ipv6/ietf-ip:address[ip='2001:db8::1']/ietf-ip:prefix-length", "32");
501 REQUIRE_THROWS_AS(client.applyChanges(), sysrepo::ErrorWithCode);
Tomáš Peckac2929a62021-06-05 17:21:03 +0200502 }
503
504 SECTION("Can't be a slave when both IPv4 and IPv6 enabled")
505 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100506 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24");
507 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:address[ip='2001:db8::1']/ietf-ip:prefix-length", "32");
508 REQUIRE_THROWS_AS(client.applyChanges(), sysrepo::ErrorWithCode);
Tomáš Peckac2929a62021-06-05 17:21:03 +0200509 }
510
511 SECTION("Can be a slave when addresses present but protocol is disabled")
512 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100513 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/ietf-ip:prefix-length", "24");
514 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/enabled", "false");
Tomáš Peckac2929a62021-06-05 17:21:03 +0200515
516 REQUIRE_CALL(fake, cb(std::vector<std::string>{"br0", "eth0"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100517 client.applyChanges();
Tomáš Peckac2929a62021-06-05 17:21:03 +0200518
519 // reset the contents
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100520 client.deleteItem("/ietf-interfaces:interfaces/interface[name='br0']");
521 client.deleteItem("/ietf-interfaces:interfaces/interface[name='eth0']");
Tomáš Peckac2929a62021-06-05 17:21:03 +0200522 REQUIRE_CALL(fake, cb(std::vector<std::string>{"br0", "eth0"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100523 client.applyChanges();
Tomáš Peckac2929a62021-06-05 17:21:03 +0200524 REQUIRE(!std::filesystem::exists(expectedFilePathBr0));
525 REQUIRE(!std::filesystem::exists(expectedFilePathEth0));
526 }
527 }
Tomáš Peckabd43a942021-08-04 10:12:49 +0200528
529 SECTION("Network autoconfiguration")
530 {
531 const auto expectedFilePath = fakeConfigDir / "eth0.network";
532
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100533 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/type", "iana-if-type:ethernetCsmacd");
Tomáš Peckabd43a942021-08-04 10:12:49 +0200534
535 SECTION("IPv4 on with address, IPv6 disabled, DHCPv4 off, RA off")
536 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100537 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "false");
538 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 +0200539
540 expectedContents = R"([Match]
541Name=eth0
542
543[Network]
544Address=192.0.2.1/24
545LinkLocalAddressing=no
546IPv6AcceptRA=false
547DHCP=no
548LLDP=true
549EmitLLDP=nearest-bridge
550)";
551 }
552
553 SECTION("IPv4 on with address, IPv6 disabled, DHCPv4 on, RA on")
554 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100555 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "true");
556 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
557 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:enabled", "false");
558 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 +0200559
560 expectedContents = R"([Match]
561Name=eth0
562
563[Network]
564Address=192.0.2.1/24
565LinkLocalAddressing=no
566IPv6AcceptRA=false
567DHCP=ipv4
568LLDP=true
569EmitLLDP=nearest-bridge
570)";
571 }
572
573 SECTION("IPv4 disabled, IPv6 enabled, DHCPv4 on, RA on")
574 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100575 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:enabled", "false");
576 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "true");
577 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:enabled", "true");
578 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 +0200579
580 expectedContents = R"([Match]
581Name=eth0
582
583[Network]
584IPv6AcceptRA=true
585DHCP=no
586LLDP=true
587EmitLLDP=nearest-bridge
588)";
589 }
590
591 SECTION("IPv4 enabled, IPv6 enabled, DHCPv4 on, RA on")
592 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100593 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:enabled", "true");
594 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "true");
595 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:enabled", "true");
596 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 +0200597
598 expectedContents = R"([Match]
599Name=eth0
600
601[Network]
602IPv6AcceptRA=true
603DHCP=ipv4
604LLDP=true
605EmitLLDP=nearest-bridge
606)";
607 }
608
609 SECTION("IPv4 enabled, IPv6 enabled, DHCPv4 off, RA on")
610 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100611 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:enabled", "true");
612 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/prefix-length", "24");
613 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "false");
614 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:enabled", "true");
615 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 +0200616
617 expectedContents = R"([Match]
618Name=eth0
619
620[Network]
621Address=192.0.2.1/24
622IPv6AcceptRA=true
623DHCP=no
624LLDP=true
625EmitLLDP=nearest-bridge
626)";
627 }
628
629 SECTION("IPv4 disabled, IPv6 disabled, DHCPv4 off, RA off")
630 {
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100631 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/ietf-ip:address[ip='192.0.2.1']/prefix-length", "24");
632 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv4/czechlight-network:dhcp-client", "false");
633 client.setItem("/ietf-interfaces:interfaces/interface[name='eth0']/ietf-ip:ipv6/ietf-ip:address[ip='2001:db8::1']/prefix-length", "32");
634 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 +0200635
636 expectedContents = R"([Match]
637Name=eth0
638
639[Network]
640Address=192.0.2.1/24
641Address=2001:db8::1/32
642IPv6AcceptRA=false
643DHCP=no
644LLDP=true
645EmitLLDP=nearest-bridge
646)";
647 }
648
649 REQUIRE_CALL(fake, cb(std::vector<std::string>{"eth0"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100650 client.applyChanges();
Tomáš Peckabd43a942021-08-04 10:12:49 +0200651 REQUIRE(std::filesystem::exists(expectedFilePath));
652 REQUIRE(velia::utils::readFileToString(expectedFilePath) == expectedContents);
653
654 // reset the contents
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100655 client.deleteItem("/ietf-interfaces:interfaces/interface[name='eth0']");
Tomáš Peckabd43a942021-08-04 10:12:49 +0200656 REQUIRE_CALL(fake, cb(std::vector<std::string>{"eth0"})).IN_SEQUENCE(seq1);
Václav Kubernát7efd6d52021-11-09 01:31:11 +0100657 client.applyChanges();
Tomáš Peckabd43a942021-08-04 10:12:49 +0200658 REQUIRE(!std::filesystem::exists(expectedFilePath));
659 }
Tomáš Pecka39bbb512021-05-23 22:07:22 +0200660}