system: Don't reconfigure network interface if not necessary
It is not necessary to reload network if the new setting is the same as
the current one because the network outage may be long (locally it is
around 6 seconds).
This issue can happen on system startup.
1. cfg-restore-systemd-networkd service copies a configuration from /cfg
to /run/systemd/network directory.
2. velia-system starts up and try to reconfigure the network by writing
a .network file to the same directory. The content of the file is
generated from the setting in the running datastore.
However, this setting *should* be the same as the setting copied from /cfg
and therefore there is no point in reconfiguring the network.
It'd be different only if startup datastore and data in /cfg are, for some
reason, not synchronized (e.g. someone removes the file from /cfg).
Change-Id: I83b2f8fbcc61962ab7b264d591f546b4a3119590
diff --git a/src/system/Network.cpp b/src/system/Network.cpp
index cb8254a..055c3e1 100644
--- a/src/system/Network.cpp
+++ b/src/system/Network.cpp
@@ -53,13 +53,18 @@
CZECHLIGHT_SYSTEM_MODULE_NAME.c_str(),
[&, networkConfigDirectory = std::move(networkConfigDirectory), networkReloadCallback = std::move(networkReloadCallback)](sysrepo::S_Session session, [[maybe_unused]] const char* module_name, [[maybe_unused]] const char* xpath, [[maybe_unused]] sr_event_t event, [[maybe_unused]] uint32_t request_id) {
auto config = getNetworkConfiguration(session, m_log);
+ std::vector<std::string> changedInterfaces;
+
for (const auto& [interface, networkFileContents] : config) {
- velia::utils::safeWriteFile(networkConfigDirectory / (interface + ".network"), networkFileContents);
+ auto targetFile = networkConfigDirectory / (interface + ".network");
+
+ if (!std::filesystem::exists(targetFile) || velia::utils::readFileToString(targetFile) != networkFileContents) { // don't reload if the new file same as the already existing file
+ velia::utils::safeWriteFile(targetFile, networkFileContents);
+ changedInterfaces.push_back(interface);
+ }
}
- std::vector<std::string> interfaces;
- std::transform(config.begin(), config.end(), std::back_inserter(interfaces), [](const auto& kv) { return kv.first; });
- networkReloadCallback(interfaces);
+ networkReloadCallback(changedInterfaces);
return SR_ERR_OK;
},
diff --git a/tests/sysrepo_system-network.cpp b/tests/sysrepo_system-network.cpp
index ee71ff4..6feeed2 100644
--- a/tests/sysrepo_system-network.cpp
+++ b/tests/sysrepo_system-network.cpp
@@ -7,6 +7,7 @@
#include "trompeloeil_doctest.h"
#include <filesystem>
+#include "fs-helpers/FileInjector.h"
#include "fs-helpers/utils.h"
#include "pretty_printers.h"
#include "system/Network.h"
@@ -66,28 +67,84 @@
client->delete_item(PRESENCE_CONTAINER);
client->apply_changes();
- REQUIRE_CALL(fake, cb(std::vector<std::string> {"eth1"})).IN_SEQUENCE(seq1);
- auto network = std::make_shared<velia::system::Network>(srSess, fakeDir, [&fake](const std::vector<std::string>& updatedInterfaces) { fake.cb(updatedInterfaces); });
-
- REQUIRE(std::filesystem::exists(expectedFilePath));
- REQUIRE(velia::utils::readFileToString(expectedFilePath) == EXPECTED_CONTENT_BRIDGE);
-
- SECTION("Nothing happens")
- {
- }
-
- SECTION("Change: Container present. Switch to DHCP configuration")
+ SECTION("File not present")
{
REQUIRE_CALL(fake, cb(std::vector<std::string> {"eth1"})).IN_SEQUENCE(seq1);
-
- client->set_item(PRESENCE_CONTAINER);
- client->apply_changes();
- waitForCompletionAndBitMore(seq1);
+ auto network = std::make_shared<velia::system::Network>(srSess, fakeDir, [&fake](const std::vector<std::string>& updatedInterfaces) { fake.cb(updatedInterfaces); });
REQUIRE(std::filesystem::exists(expectedFilePath));
- REQUIRE(velia::utils::readFileToString(expectedFilePath) == EXPECTED_CONTENT_DHCP);
+ REQUIRE(velia::utils::readFileToString(expectedFilePath) == EXPECTED_CONTENT_BRIDGE);
+
+ SECTION("Nothing happens")
+ {
+ }
+
+ SECTION("Change: Container present. Switch to DHCP configuration")
+ {
+ REQUIRE_CALL(fake, cb(std::vector<std::string> {"eth1"})).IN_SEQUENCE(seq1);
+
+ client->set_item(PRESENCE_CONTAINER);
+ client->apply_changes();
+ waitForCompletionAndBitMore(seq1);
+
+ REQUIRE(std::filesystem::exists(expectedFilePath));
+ REQUIRE(velia::utils::readFileToString(expectedFilePath) == EXPECTED_CONTENT_DHCP);
+ }
}
+ SECTION("Configuration file already present (with DHCP configuration)")
+ {
+ auto file = std::make_unique<FileInjector>(expectedFilePath, std::filesystem::perms::all, EXPECTED_CONTENT_DHCP);
+ REQUIRE_CALL(fake, cb(std::vector<std::string>{"eth1"})).IN_SEQUENCE(seq1);
+ spdlog::get("main")->error("CONTENT: {}", velia::utils::readFileToString(expectedFilePath));
+ auto network = std::make_shared<velia::system::Network>(srSess, fakeDir, [&fake](const std::vector<std::string>& updatedInterfaces) { fake.cb(updatedInterfaces); });
+
+ REQUIRE(std::filesystem::exists(expectedFilePath));
+ REQUIRE(velia::utils::readFileToString(expectedFilePath) == EXPECTED_CONTENT_BRIDGE);
+
+ SECTION("Nothing happens")
+ {
+ }
+
+ SECTION("Change: Container present. Switch to DHCP configuration")
+ {
+ REQUIRE_CALL(fake, cb(std::vector<std::string> {"eth1"})).IN_SEQUENCE(seq1);
+
+ client->set_item(PRESENCE_CONTAINER);
+ client->apply_changes();
+ waitForCompletionAndBitMore(seq1);
+
+ REQUIRE(std::filesystem::exists(expectedFilePath));
+ REQUIRE(velia::utils::readFileToString(expectedFilePath) == EXPECTED_CONTENT_DHCP);
+ }
+ }
+
+ SECTION("Configuration file already present (with bridge configuration)")
+ {
+ auto file = std::make_unique<FileInjector>(expectedFilePath, std::filesystem::perms::all, EXPECTED_CONTENT_BRIDGE);
+ REQUIRE_CALL(fake, cb(std::vector<std::string>{})).IN_SEQUENCE(seq1);
+ spdlog::get("main")->error("CONTENT: {}", velia::utils::readFileToString(expectedFilePath));
+ auto network = std::make_shared<velia::system::Network>(srSess, fakeDir, [&fake](const std::vector<std::string>& updatedInterfaces) { fake.cb(updatedInterfaces); });
+
+ REQUIRE(std::filesystem::exists(expectedFilePath));
+ REQUIRE(velia::utils::readFileToString(expectedFilePath) == EXPECTED_CONTENT_BRIDGE);
+
+ SECTION("Nothing happens")
+ {
+ }
+
+ SECTION("Change: Container present. Switch to DHCP configuration")
+ {
+ REQUIRE_CALL(fake, cb(std::vector<std::string> {"eth1"})).IN_SEQUENCE(seq1);
+
+ client->set_item(PRESENCE_CONTAINER);
+ client->apply_changes();
+ waitForCompletionAndBitMore(seq1);
+
+ REQUIRE(std::filesystem::exists(expectedFilePath));
+ REQUIRE(velia::utils::readFileToString(expectedFilePath) == EXPECTED_CONTENT_DHCP);
+ }
+ }
}
SECTION("Container present. Start with DHCP configuration")