Merge changes Ic2bdd047,If20abcda,I90edcf87,I6c42ae2b,I703c6982, ...

* changes:
  system: fix log level setting in velia-system
  yang: disable ipv6/autoconf in ietf-ip model
  utils: Fix no data in valuesPush results in segfault
  system: Implement ietf-interfaces
  system: Get link info from rtnetlink
  utils: log libyang manipulation
  utils: Allow to remove values from sysrepo
  CI: install libnl
  yang: czechlight-network (ietf-interfaces, ietf-ip) model
diff --git a/ci/build.sh b/ci/build.sh
index 5cc60ab..2631003 100755
--- a/ci/build.sh
+++ b/ci/build.sh
@@ -19,13 +19,6 @@
     export CC=clang
     export CXX=clang++
     export LD=clang
-    export CXXFLAGS="-stdlib=libc++"
-    # spdlog passes std::string instances, and it's built against GCC's libstdc++ on the CI
-    sed -i \
-        -e 's/spdlog::spdlog/spdlog::spdlog_header_only/' \
-        -e '/find_package(spdlog/ a add_definitions(-DFMT_HEADER_ONLY)' \
-        -e 's/fmt::fmt/fmt::fmt-header-only/' \
-        ${ZUUL_PROJECT_SRC_DIR}/CMakeLists.txt
 fi
 
 if [[ $ZUUL_JOB_NAME =~ .*-ubsan ]]; then
diff --git a/src/main-system.cpp b/src/main-system.cpp
index f725d8e..4087607 100644
--- a/src/main-system.cpp
+++ b/src/main-system.cpp
@@ -71,8 +71,7 @@
         std::filesystem::create_directories(persistentNetworkDirectory);
 
         auto srSessStartup = std::make_shared<sysrepo::Session>(srConn, SR_DS_STARTUP);
-
-        auto sysrepoNetworkStartup = velia::system::Network(srSess, persistentNetworkDirectory, [](const auto&) {});
+        auto sysrepoNetworkStartup = velia::system::Network(srSessStartup, persistentNetworkDirectory, [](const auto&) {});
         auto sysrepoNetworkRunning = velia::system::Network(srSess, runtimeNetworkDirectory, [](const auto& reconfiguredInterfaces) {
             auto log = spdlog::get("system");
 
diff --git a/src/system/Firmware.cpp b/src/system/Firmware.cpp
index 844ef47..5408c43 100644
--- a/src/system/Firmware.cpp
+++ b/src/system/Firmware.cpp
@@ -133,12 +133,23 @@
     for (const auto& slotName : FIRMWARE_SLOTS) {
         if (auto it = slotStatus.find(slotName); it != slotStatus.end()) { // if there is an update for the slot "slotName"
             const auto& props = it->second;
-            auto xpathPrefix = CZECHLIGHT_SYSTEM_FIRMWARE_MODULE_PREFIX + "firmware-slot[name='" + std::get<std::string>(props.at("bootname")) + "']/";
+            std::string xpathPrefix;
 
-            m_slotStatusCache[xpathPrefix + "state"] = std::get<std::string>(props.at("state"));
-            m_slotStatusCache[xpathPrefix + "version"] = std::get<std::string>(props.at("bundle.version"));
-            m_slotStatusCache[xpathPrefix + "installed"] = std::get<std::string>(props.at("installed.timestamp"));
-            m_slotStatusCache[xpathPrefix + "boot-status"] = std::get<std::string>(props.at("boot-status"));
+            // Better be defensive about provided properties. If somebody removes /slot.raucs, RAUC doesn't provide all the data (at least bundle.version and installed.timestamp).
+            if (auto pit = props.find("bootname"); pit != props.end()) {
+                xpathPrefix = CZECHLIGHT_SYSTEM_FIRMWARE_MODULE_PREFIX + "firmware-slot[name='" + std::get<std::string>(pit->second) + "']/";
+            } else {
+                m_log->error("RAUC didn't provide 'bootname' property for slot '{}'. Skipping update for that slot.");
+                continue;
+            }
+
+            for (const auto& [yangKey, raucKey] : {std::pair{"state", "state"}, {"boot-status", "boot-status"}, {"version", "bundle.version"}, {"installed", "installed.timestamp"}}) {
+                if (auto pit = props.find(raucKey); pit != props.end()) {
+                    m_slotStatusCache[xpathPrefix + yangKey] = std::get<std::string>(pit->second);
+                } else {
+                    m_log->warn("RAUC didn't provide '{}' property for slot '{}'.", raucKey, slotName);
+                }
+            }
         }
     }
 
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-firmware.cpp b/tests/sysrepo_system-firmware.cpp
index 6776518..ef470f1 100644
--- a/tests/sysrepo_system-firmware.cpp
+++ b/tests/sysrepo_system-firmware.cpp
@@ -9,7 +9,7 @@
 
 using namespace std::literals;
 
-TEST_CASE("Firmware in czechlight-system")
+TEST_CASE("Firmware in czechlight-system, RPC")
 {
     trompeloeil::sequence seq1;
 
@@ -245,3 +245,198 @@
         }
     }
 }
+
+TEST_CASE("Firmware in czechlight-system, slot status")
+{
+    trompeloeil::sequence seq1;
+
+    TEST_SYSREPO_INIT_LOGS;
+    TEST_SYSREPO_INIT;
+    TEST_SYSREPO_INIT_CLIENT;
+
+    auto dbusServerConnection = sdbus::createSessionBusConnection("de.pengutronix.rauc");
+    auto dbusClientConnectionSignals = sdbus::createSessionBusConnection();
+    auto dbusClientConnectionMethods = sdbus::createSessionBusConnection();
+    dbusClientConnectionSignals->enterEventLoopAsync();
+    dbusClientConnectionMethods->enterEventLoopAsync();
+    dbusServerConnection->enterEventLoopAsync();
+
+    std::map<std::string, velia::system::RAUC::SlotProperties> dbusRaucStatus;
+    std::map<std::string, std::string> expected;
+
+    SECTION("Complete data")
+    {
+        dbusRaucStatus = {
+            {"rootfs.1", {
+                             {"activated.count", uint32_t {39}},
+                             {"activated.timestamp", "2021-01-13T17:20:18Z"},
+                             {"bootname", "B"},
+                             {"boot-status", "good"},
+                             {"bundle.compatible", "czechlight-clearfog"},
+                             {"bundle.version", "v4-103-g34d2f48"},
+                             {"class", "rootfs"},
+                             {"device", "/dev/mmcblk0p3"},
+                             {"installed.count", uint32_t {39}},
+                             {"installed.timestamp", "2021-01-13T17:20:15Z"},
+                             {"mountpoint", "/"},
+                             {"sha256", "07b30d065c7aad64d2006ce99fd339c929d3ca97b666fca4584b9ef726469fc4"},
+                             {"size", uint64_t {45601892}},
+                             {"state", "booted"},
+                             {"status", "ok"},
+                             {"type", "ext4"},
+                         }},
+            {"rootfs.0", {
+                             {"activated.count", uint32_t {41}},
+                             {"activated.timestamp", "2021-01-13T17:15:54Z"},
+                             {"bootname", "A"},
+                             {"boot-status", "bad"},
+                             {"bundle.compatible", "czechlight-clearfog"},
+                             {"bundle.version", "v4-104-ge80fcd4"},
+                             {"class", "rootfs"},
+                             {"device", "/dev/mmcblk0p1"},
+                             {"installed.count", uint32_t {41}},
+                             {"installed.timestamp", "2021-01-13T17:15:50Z"},
+                             {"sha256", "6d81e8f341edd17c127811f7347c7e23d18c2fc25c0bdc29ac56999cc9c25629"},
+                             {"size", uint64_t {45647664}},
+                             {"state", "inactive"},
+                             {"status", "ok"},
+                             {"type", "ext4"},
+                         }},
+            {"cfg.1", {
+                             {"bundle.compatible", "czechlight-clearfog"},
+                             {"bundle.version", "v4-103-g34d2f48"},
+                             {"class", "cfg"},
+                             {"device", "/dev/mmcblk0p4"},
+                             {"installed.count", uint32_t {39}},
+                             {"installed.timestamp", "2021-01-13T17:20:18Z"},
+                             {"mountpoint", "/cfg"},
+                             {"parent", "rootfs.1"},
+                             {"sha256", "5ca1b6c461fc194055d52b181f57c63dc1d34c19d041f6395e6f6abc039692bb"},
+                             {"size", uint64_t {108}},
+                             {"state", "active"},
+                             {"status", "ok"},
+                             {"type", "ext4"},
+                         }},
+            {"cfg.0", {
+                             {"bundle.compatible", "czechlight-clearfog"},
+                             {"bundle.version", "v4-104-ge80fcd4"},
+                             {"class", "cfg"},
+                             {"device", "/dev/mmcblk0p2"},
+                             {"installed.count", uint32_t {41}},
+                             {"installed.timestamp", "2021-01-13T17:15:54Z"},
+                             {"parent", "rootfs.0"},
+                             {"sha256", "5ca1b6c461fc194055d52b181f57c63dc1d34c19d041f6395e6f6abc039692bb"},
+                             {"size", uint64_t {108}},
+                             {"state", "inactive"},
+                             {"status", "ok"},
+                             {"type", "ext4"},
+                         }},
+        };
+
+        expected = {
+            {"[name='A']/boot-status", "bad"},
+            {"[name='A']/installed", "2021-01-13T17:15:50Z"},
+            {"[name='A']/name", "A"},
+            {"[name='A']/state", "inactive"},
+            {"[name='A']/version", "v4-104-ge80fcd4"},
+            {"[name='B']/boot-status", "good"},
+            {"[name='B']/installed", "2021-01-13T17:20:15Z"},
+            {"[name='B']/name", "B"},
+            {"[name='B']/state", "booted"},
+            {"[name='B']/version", "v4-103-g34d2f48"},
+        };
+    }
+
+    SECTION("Missing data in rootfs.0")
+    {
+        dbusRaucStatus = {
+            {"rootfs.1", {
+                             {"activated.count", uint32_t {39}},
+                             {"activated.timestamp", "2021-01-13T17:20:18Z"},
+                             {"bootname", "B"},
+                             {"boot-status", "good"},
+                             {"bundle.compatible", "czechlight-clearfog"},
+                             {"bundle.version", "v4-103-g34d2f48"},
+                             {"class", "rootfs"},
+                             {"device", "/dev/mmcblk0p3"},
+                             {"installed.count", uint32_t {39}},
+                             {"installed.timestamp", "2021-01-13T17:20:15Z"},
+                             {"mountpoint", "/"},
+                             {"sha256", "07b30d065c7aad64d2006ce99fd339c929d3ca97b666fca4584b9ef726469fc4"},
+                             {"size", uint64_t {45601892}},
+                             {"state", "booted"},
+                             {"status", "ok"},
+                             {"type", "ext4"},
+                         }},
+            {"rootfs.0", {
+                             {"bootname", "A"},
+                             {"boot-status", "bad"},
+                             {"class", "rootfs"},
+                             {"device", "/dev/mmcblk0p1"},
+                             {"sha256", "6d81e8f341edd17c127811f7347c7e23d18c2fc25c0bdc29ac56999cc9c25629"},
+                             {"size", uint64_t {45647664}},
+                             {"state", "inactive"},
+                             {"status", "ok"},
+                             {"type", "ext4"},
+                         }},
+        };
+
+        expected = {
+            {"[name='A']/boot-status", "bad"},
+            {"[name='A']/name", "A"},
+            {"[name='A']/state", "inactive"},
+            {"[name='B']/boot-status", "good"},
+            {"[name='B']/installed", "2021-01-13T17:20:15Z"},
+            {"[name='B']/name", "B"},
+            {"[name='B']/state", "booted"},
+            {"[name='B']/version", "v4-103-g34d2f48"},
+        };
+    }
+
+    SECTION("Missing bootname in rootfs.0")
+    {
+        dbusRaucStatus = {
+            {"rootfs.1", {
+                             {"activated.count", uint32_t {39}},
+                             {"activated.timestamp", "2021-01-13T17:20:18Z"},
+                             {"bootname", "B"},
+                             {"boot-status", "good"},
+                             {"bundle.compatible", "czechlight-clearfog"},
+                             {"bundle.version", "v4-103-g34d2f48"},
+                             {"class", "rootfs"},
+                             {"device", "/dev/mmcblk0p3"},
+                             {"installed.count", uint32_t {39}},
+                             {"installed.timestamp", "2021-01-13T17:20:15Z"},
+                             {"mountpoint", "/"},
+                             {"sha256", "07b30d065c7aad64d2006ce99fd339c929d3ca97b666fca4584b9ef726469fc4"},
+                             {"size", uint64_t {45601892}},
+                             {"state", "booted"},
+                             {"status", "ok"},
+                             {"type", "ext4"},
+                         }},
+            {"rootfs.0", {
+                             {"boot-status", "bad"},
+                             {"class", "rootfs"},
+                             {"device", "/dev/mmcblk0p1"},
+                             {"sha256", "6d81e8f341edd17c127811f7347c7e23d18c2fc25c0bdc29ac56999cc9c25629"},
+                             {"size", uint64_t {45647664}},
+                             {"state", "inactive"},
+                             {"status", "ok"},
+                             {"type", "ext4"},
+                         }},
+        };
+
+        expected = {
+            {"[name='B']/boot-status", "good"},
+            {"[name='B']/installed", "2021-01-13T17:20:15Z"},
+            {"[name='B']/name", "B"},
+            {"[name='B']/state", "booted"},
+            {"[name='B']/version", "v4-103-g34d2f48"},
+        };
+    }
+
+    auto raucServer = DBusRAUCServer(*dbusServerConnection, "rootfs.1", dbusRaucStatus);
+    auto sysrepo = std::make_shared<velia::system::Firmware>(srConn, *dbusClientConnectionSignals, *dbusClientConnectionMethods);
+
+    REQUIRE(dataFromSysrepo(client, "/czechlight-system:firmware/firmware-slot", SR_DS_OPERATIONAL) == expected);
+}
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")