Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 1 | /* |
| 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" |
| 9 | #include <boost/algorithm/string/join.hpp> |
| 10 | #include <boost/process.hpp> |
| 11 | #include <boost/process/extend.hpp> |
| 12 | #include <cstdlib> |
| 13 | #include <netlink/route/addr.h> |
| 14 | #include <regex> |
| 15 | #include <sys/wait.h> |
| 16 | #include <thread> |
| 17 | #include "pretty_printers.h" |
| 18 | #include "system/IETFInterfaces.h" |
| 19 | #include "test_log_setup.h" |
| 20 | #include "test_sysrepo_helpers.h" |
| 21 | #include "test_vars.h" |
| 22 | #include "utils/exec.h" |
| 23 | |
| 24 | using namespace std::chrono_literals; |
| 25 | using namespace std::string_literals; |
| 26 | |
| 27 | namespace { |
| 28 | |
| 29 | const auto IFACE = "czechlight0"s; |
| 30 | const auto LINK_MAC = "02:02:02:02:02:02"s; |
Jan Kundrát | 09fc701 | 2023-01-17 00:25:55 +0100 | [diff] [blame] | 31 | const auto WAIT = 500ms; |
Tomáš Pecka | 9e2291b | 2021-07-14 20:39:30 +0200 | [diff] [blame] | 32 | const auto WAIT_BRIDGE = 2500ms; |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 33 | |
| 34 | template <class... Args> |
| 35 | void iproute2_run(const Args... args_) |
| 36 | { |
| 37 | namespace bp = boost::process; |
| 38 | auto logger = spdlog::get("main"); |
| 39 | |
| 40 | bp::ipstream stdoutStream; |
| 41 | bp::ipstream stderrStream; |
| 42 | |
| 43 | std::vector<std::string> args = {IPROUTE2_EXECUTABLE, args_...}; |
| 44 | |
Tomáš Pecka | fd5ec7b | 2023-02-02 18:48:37 +0100 | [diff] [blame] | 45 | logger->trace("exec: {}", boost::algorithm::join(args, " ")); |
| 46 | bp::child c(boost::process::args = std::move(args), bp::std_out > stdoutStream, bp::std_err > stderrStream); |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 47 | c.wait(); |
Tomáš Pecka | fd5ec7b | 2023-02-02 18:48:37 +0100 | [diff] [blame] | 48 | logger->trace("{} exited", IPROUTE2_EXECUTABLE); |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 49 | |
| 50 | if (c.exit_code() != 0) { |
| 51 | std::istreambuf_iterator<char> begin(stderrStream), end; |
| 52 | std::string stderrOutput(begin, end); |
Tomáš Pecka | fd5ec7b | 2023-02-02 18:48:37 +0100 | [diff] [blame] | 53 | logger->critical("{} ended with a non-zero exit code. stderr: {}", IPROUTE2_EXECUTABLE, stderrOutput); |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 54 | |
Tomáš Pecka | fd5ec7b | 2023-02-02 18:48:37 +0100 | [diff] [blame] | 55 | throw std::runtime_error(IPROUTE2_EXECUTABLE + " returned non-zero exit code "s + std::to_string(c.exit_code())); |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 56 | } |
| 57 | } |
| 58 | |
| 59 | template <class... Args> |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 60 | void iproute2_exec_and_wait(const auto& wait, const Args... args_) |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 61 | { |
| 62 | iproute2_run(args_...); |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 63 | std::this_thread::sleep_for(wait); // wait for velia to process and publish the change |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 64 | } |
| 65 | |
| 66 | |
| 67 | template <class T> |
| 68 | void nlCacheForeachWrapper(nl_cache* cache, std::function<void(T*)> cb) |
| 69 | { |
| 70 | nl_cache_foreach( |
| 71 | cache, [](nl_object* obj, void* data) { |
| 72 | auto& cb = *static_cast<std::function<void(T*)>*>(data); |
| 73 | auto link = reinterpret_cast<T*>(obj); |
| 74 | cb(link); |
| 75 | }, |
| 76 | &cb); |
| 77 | } |
| 78 | |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 79 | auto dataFromSysrepoNoStatistics(sysrepo::Session session, const std::string& xpath, sysrepo::Datastore datastore) |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 80 | { |
| 81 | auto res = dataFromSysrepo(session, xpath, datastore); |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 82 | REQUIRE(res.erase("/statistics/in-octets") == 1); |
| 83 | REQUIRE(res.erase("/statistics/in-errors") == 1); |
| 84 | REQUIRE(res.erase("/statistics/in-discards") == 1); |
| 85 | REQUIRE(res.erase("/statistics/out-octets") == 1); |
| 86 | REQUIRE(res.erase("/statistics/out-errors") == 1); |
| 87 | REQUIRE(res.erase("/statistics/out-discards") == 1); |
| 88 | return res; |
| 89 | } |
| 90 | |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 91 | } |
| 92 | |
| 93 | TEST_CASE("Test ietf-interfaces and ietf-routing") |
| 94 | { |
| 95 | TEST_SYSREPO_INIT_LOGS; |
| 96 | TEST_SYSREPO_INIT; |
| 97 | TEST_SYSREPO_INIT_CLIENT; |
| 98 | |
| 99 | auto network = std::make_shared<velia::system::IETFInterfaces>(srSess); |
| 100 | |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 101 | iproute2_exec_and_wait(WAIT, "link", "add", IFACE, "address", LINK_MAC, "type", "dummy"); |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 102 | |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 103 | iproute2_exec_and_wait(WAIT, "addr", "add", "192.0.2.1/24", "dev", IFACE); // from TEST-NET-1 (RFC 5737) |
| 104 | iproute2_exec_and_wait(WAIT, "addr", "add", "::ffff:192.0.2.1", "dev", IFACE); |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 105 | |
| 106 | std::map<std::string, std::string> initialExpected{ |
| 107 | {"/ietf-ip:ipv4", ""}, |
| 108 | {"/ietf-ip:ipv4/address[ip='192.0.2.1']", ""}, |
| 109 | {"/ietf-ip:ipv4/address[ip='192.0.2.1']/ip", "192.0.2.1"}, |
| 110 | {"/ietf-ip:ipv4/address[ip='192.0.2.1']/prefix-length", "24"}, |
| 111 | {"/ietf-ip:ipv6", ""}, |
| 112 | {"/ietf-ip:ipv6/address[ip='::ffff:192.0.2.1']", ""}, |
| 113 | {"/ietf-ip:ipv6/address[ip='::ffff:192.0.2.1']/ip", "::ffff:192.0.2.1"}, |
| 114 | {"/ietf-ip:ipv6/address[ip='::ffff:192.0.2.1']/prefix-length", "128"}, |
Tomáš Pecka | bd43a94 | 2021-08-04 10:12:49 +0200 | [diff] [blame] | 115 | {"/ietf-ip:ipv6/autoconf", ""}, |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 116 | {"/name", IFACE}, |
| 117 | {"/oper-status", "down"}, |
| 118 | {"/phys-address", LINK_MAC}, |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 119 | {"/statistics", ""}, |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 120 | {"/type", "iana-if-type:ethernetCsmacd"}, |
| 121 | }; |
| 122 | |
| 123 | SECTION("Change physical address") |
| 124 | { |
| 125 | const auto LINK_MAC_CHANGED = "02:44:44:44:44:44"s; |
| 126 | |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 127 | iproute2_exec_and_wait(WAIT, "link", "set", IFACE, "address", LINK_MAC_CHANGED); |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 128 | |
| 129 | std::map<std::string, std::string> expected = initialExpected; |
| 130 | expected["/phys-address"] = LINK_MAC_CHANGED; |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 131 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE + "']", sysrepo::Datastore::Operational) == expected); |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 132 | } |
| 133 | |
| 134 | SECTION("Add and remove IP addresses") |
| 135 | { |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 136 | iproute2_exec_and_wait(WAIT, "addr", "add", "192.0.2.6/24", "dev", IFACE); |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 137 | std::map<std::string, std::string> expected = initialExpected; |
| 138 | expected["/ietf-ip:ipv4/address[ip='192.0.2.6']"] = ""; |
| 139 | expected["/ietf-ip:ipv4/address[ip='192.0.2.6']/ip"] = "192.0.2.6"; |
| 140 | expected["/ietf-ip:ipv4/address[ip='192.0.2.6']/prefix-length"] = "24"; |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 141 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE + "']", sysrepo::Datastore::Operational) == expected); |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 142 | |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 143 | iproute2_exec_and_wait(WAIT, "addr", "del", "192.0.2.6/24", "dev", IFACE); |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 144 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE + "']", sysrepo::Datastore::Operational) == initialExpected); |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 145 | } |
| 146 | |
| 147 | SECTION("IPv6 LL gained when device up") |
| 148 | { |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 149 | iproute2_exec_and_wait(WAIT, "link", "set", "dev", IFACE, "up"); |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 150 | |
| 151 | { |
| 152 | std::map<std::string, std::string> expected = initialExpected; |
| 153 | expected["/ietf-ip:ipv6/address[ip='fe80::2:2ff:fe02:202']"] = ""; |
| 154 | expected["/ietf-ip:ipv6/address[ip='fe80::2:2ff:fe02:202']/ip"] = "fe80::2:2ff:fe02:202"; |
| 155 | expected["/ietf-ip:ipv6/address[ip='fe80::2:2ff:fe02:202']/prefix-length"] = "64"; |
| 156 | expected["/oper-status"] = "unknown"; |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 157 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE + "']", sysrepo::Datastore::Operational) == expected); |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 158 | } |
| 159 | |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 160 | iproute2_exec_and_wait(WAIT, "link", "set", "dev", IFACE, "down"); // this discards all addresses, i.e., the link-local address and the ::ffff:192.0.2.1 address |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 161 | { |
| 162 | std::map<std::string, std::string> expected = initialExpected; |
| 163 | expected.erase("/ietf-ip:ipv6/address[ip='::ffff:192.0.2.1']"); |
| 164 | expected.erase("/ietf-ip:ipv6/address[ip='::ffff:192.0.2.1']/ip"); |
| 165 | expected.erase("/ietf-ip:ipv6/address[ip='::ffff:192.0.2.1']/prefix-length"); |
| 166 | expected["/oper-status"] = "down"; |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 167 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE + "']", sysrepo::Datastore::Operational) == expected); |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 168 | } |
| 169 | } |
| 170 | |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 171 | SECTION("Add a bridge") |
| 172 | { |
| 173 | const auto IFACE_BRIDGE = "czechlight_br0"s; |
| 174 | const auto MAC_BRIDGE = "02:22:22:22:22:22"; |
| 175 | |
| 176 | std::map<std::string, std::string> expectedIface = initialExpected; |
| 177 | std::map<std::string, std::string> expectedBridge{ |
| 178 | {"/name", "czechlight_br0"}, |
| 179 | {"/oper-status", "down"}, |
| 180 | {"/phys-address", MAC_BRIDGE}, |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 181 | {"/statistics", ""}, |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 182 | {"/type", "iana-if-type:bridge"}, |
| 183 | }; |
| 184 | |
| 185 | iproute2_exec_and_wait(WAIT, "link", "add", "name", IFACE_BRIDGE, "address", MAC_BRIDGE, "type", "bridge"); |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 186 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE + "']", sysrepo::Datastore::Operational) == expectedIface); |
| 187 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE_BRIDGE + "']", sysrepo::Datastore::Operational) == expectedBridge); |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 188 | |
| 189 | iproute2_exec_and_wait(WAIT, "link", "set", "dev", IFACE, "master", IFACE_BRIDGE); |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 190 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE + "']", sysrepo::Datastore::Operational) == expectedIface); |
| 191 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE_BRIDGE + "']", sysrepo::Datastore::Operational) == expectedBridge); |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 192 | |
| 193 | iproute2_exec_and_wait(WAIT, "link", "set", "dev", IFACE, "up"); |
Tomáš Pecka | ee9fbaa | 2021-12-14 15:39:06 +0100 | [diff] [blame] | 194 | iproute2_exec_and_wait(WAIT, "addr", "flush", "dev", IFACE); // sometimes, addresses are preserved even when enslaved |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 195 | expectedIface["/oper-status"] = "unknown"; |
Tomáš Pecka | ee9fbaa | 2021-12-14 15:39:06 +0100 | [diff] [blame] | 196 | expectedIface.erase("/ietf-ip:ipv6/address[ip='::ffff:192.0.2.1']"); |
| 197 | expectedIface.erase("/ietf-ip:ipv6/address[ip='::ffff:192.0.2.1']/ip"); |
| 198 | expectedIface.erase("/ietf-ip:ipv6/address[ip='::ffff:192.0.2.1']/prefix-length"); |
| 199 | expectedIface.erase("/ietf-ip:ipv4/address[ip='192.0.2.1']"); |
| 200 | expectedIface.erase("/ietf-ip:ipv4/address[ip='192.0.2.1']/ip"); |
| 201 | expectedIface.erase("/ietf-ip:ipv4/address[ip='192.0.2.1']/prefix-length"); |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 202 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE + "']", sysrepo::Datastore::Operational) == expectedIface); |
| 203 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE_BRIDGE + "']", sysrepo::Datastore::Operational) == expectedBridge); |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 204 | |
| 205 | iproute2_exec_and_wait(WAIT_BRIDGE, "link", "set", "dev", IFACE_BRIDGE, "up"); |
Tomáš Pecka | 9e2291b | 2021-07-14 20:39:30 +0200 | [diff] [blame] | 206 | expectedBridge["/ietf-ip:ipv6"] = ""; |
Tomáš Pecka | bd43a94 | 2021-08-04 10:12:49 +0200 | [diff] [blame] | 207 | expectedBridge["/ietf-ip:ipv6/autoconf"] = ""; |
Tomáš Pecka | 9e2291b | 2021-07-14 20:39:30 +0200 | [diff] [blame] | 208 | expectedBridge["/ietf-ip:ipv6/address[ip='fe80::22:22ff:fe22:2222']"] = ""; |
| 209 | expectedBridge["/ietf-ip:ipv6/address[ip='fe80::22:22ff:fe22:2222']/ip"] = "fe80::22:22ff:fe22:2222"; |
| 210 | expectedBridge["/ietf-ip:ipv6/address[ip='fe80::22:22ff:fe22:2222']/prefix-length"] = "64"; |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 211 | expectedBridge["/oper-status"] = "up"; |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 212 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE + "']", sysrepo::Datastore::Operational) == expectedIface); |
| 213 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE_BRIDGE + "']", sysrepo::Datastore::Operational) == expectedBridge); |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 214 | |
| 215 | iproute2_exec_and_wait(WAIT_BRIDGE, "link", "set", "dev", IFACE_BRIDGE, "down"); |
Tomáš Pecka | 9e2291b | 2021-07-14 20:39:30 +0200 | [diff] [blame] | 216 | expectedBridge.erase("/ietf-ip:ipv6/address[ip='fe80::22:22ff:fe22:2222']"); |
| 217 | expectedBridge.erase("/ietf-ip:ipv6/address[ip='fe80::22:22ff:fe22:2222']/ip"); |
| 218 | expectedBridge.erase("/ietf-ip:ipv6/address[ip='fe80::22:22ff:fe22:2222']/prefix-length"); |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 219 | expectedBridge["/oper-status"] = "down"; |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 220 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE + "']", sysrepo::Datastore::Operational) == expectedIface); |
| 221 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE_BRIDGE + "']", sysrepo::Datastore::Operational) == expectedBridge); |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 222 | |
| 223 | iproute2_exec_and_wait(WAIT, "link", "set", "dev", IFACE, "down"); |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 224 | expectedIface["/oper-status"] = "down"; |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 225 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE + "']", sysrepo::Datastore::Operational) == expectedIface); |
| 226 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE_BRIDGE + "']", sysrepo::Datastore::Operational) == expectedBridge); |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 227 | iproute2_exec_and_wait(WAIT, "link", "set", "dev", IFACE, "nomaster"); |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 228 | expectedIface.erase("/ietf-ip:ipv4"); |
Tomáš Pecka | bd43a94 | 2021-08-04 10:12:49 +0200 | [diff] [blame] | 229 | expectedIface.erase("/ietf-ip:ipv6/autoconf"); |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 230 | expectedIface.erase("/ietf-ip:ipv6"); |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 231 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE + "']", sysrepo::Datastore::Operational) == expectedIface); |
| 232 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE_BRIDGE + "']", sysrepo::Datastore::Operational) == expectedBridge); |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 233 | } |
| 234 | |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 235 | SECTION("Add and remove routes") |
| 236 | { |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 237 | iproute2_exec_and_wait(WAIT, "link", "set", "dev", IFACE, "up"); |
| 238 | iproute2_exec_and_wait(WAIT, "route", "add", "198.51.100.0/24", "dev", IFACE); |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 239 | std::this_thread::sleep_for(WAIT); |
| 240 | |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 241 | auto data = dataFromSysrepo(client, "/ietf-routing:routing", sysrepo::Datastore::Operational); |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 242 | REQUIRE(data["/control-plane-protocols"] == ""); |
| 243 | REQUIRE(data["/interfaces"] == ""); |
| 244 | REQUIRE(data["/ribs"] == ""); |
| 245 | |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 246 | data = dataFromSysrepo(client, "/ietf-routing:routing/ribs/rib[name='ipv4-master']", sysrepo::Datastore::Operational); |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 247 | REQUIRE(data["/name"] == "ipv4-master"); |
| 248 | |
| 249 | auto findRouteIndex = [&data](const std::string& prefix) { |
| 250 | std::smatch match; |
| 251 | std::regex regex(R"(route\[(\d+)\])"); |
| 252 | size_t length = 0; |
| 253 | for (const auto& [key, value] : data) { |
| 254 | if (std::regex_search(key, match, regex)) { |
| 255 | length = std::max(std::stoul(match[1]), length); |
| 256 | } |
| 257 | } |
| 258 | |
| 259 | for (size_t i = 1; i <= length; i++) { |
| 260 | const auto keyPrefix = "/routes/route["s + std::to_string(i) + "]"; |
| 261 | if (data[keyPrefix + "/ietf-ipv4-unicast-routing:destination-prefix"] == prefix) |
| 262 | return i; |
| 263 | } |
| 264 | |
| 265 | return size_t{0}; |
| 266 | }; |
| 267 | |
| 268 | { |
| 269 | auto routeIdx = findRouteIndex("198.51.100.0/24"); |
| 270 | REQUIRE(routeIdx > 0); |
| 271 | REQUIRE(data["/routes/route["s + std::to_string(routeIdx) + "]/next-hop/outgoing-interface"] == IFACE); |
Jan Kundrát | 6141d58 | 2023-09-27 11:23:38 +0200 | [diff] [blame] | 272 | REQUIRE(data["/routes/route["s + std::to_string(routeIdx) + "]/source-protocol"] == "ietf-routing:static"); |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 273 | } |
| 274 | { |
| 275 | auto routeIdx = findRouteIndex("192.0.2.0/24"); |
| 276 | REQUIRE(routeIdx > 0); |
| 277 | REQUIRE(data["/routes/route["s + std::to_string(routeIdx) + "]/next-hop/outgoing-interface"] == IFACE); |
Jan Kundrát | 6141d58 | 2023-09-27 11:23:38 +0200 | [diff] [blame] | 278 | REQUIRE(data["/routes/route["s + std::to_string(routeIdx) + "]/source-protocol"] == "ietf-routing:direct"); |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 279 | } |
| 280 | |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 281 | data = dataFromSysrepo(client, "/ietf-routing:routing/ribs/rib[name='ipv6-master']", sysrepo::Datastore::Operational); |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 282 | REQUIRE(data["/name"] == "ipv6-master"); |
| 283 | |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 284 | iproute2_exec_and_wait(WAIT, "route", "del", "198.51.100.0/24"); |
| 285 | iproute2_exec_and_wait(WAIT, "link", "set", IFACE, "down"); |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 286 | } |
| 287 | |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 288 | iproute2_exec_and_wait(WAIT, "link", "del", IFACE, "type", "dummy"); // Executed later again by ctest fixture cleanup just for sure. It remains here because of doctest sections: The interface needs to be setup again. |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 289 | } |