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; |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 31 | const auto WAIT = 250ms; |
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 | |
| 45 | logger->trace("exec: {} {}", SUDO_EXECUTABLE, boost::algorithm::join(args, " ")); |
| 46 | bp::child c(SUDO_EXECUTABLE, boost::process::args = std::move(args), bp::std_out > stdoutStream, bp::std_err > stderrStream); |
| 47 | c.wait(); |
| 48 | logger->trace("{} {} exited", SUDO_EXECUTABLE, IPROUTE2_EXECUTABLE); |
| 49 | |
| 50 | if (c.exit_code() != 0) { |
| 51 | std::istreambuf_iterator<char> begin(stderrStream), end; |
| 52 | std::string stderrOutput(begin, end); |
| 53 | logger->critical("{} {} ended with a non-zero exit code. stderr: {}", SUDO_EXECUTABLE, IPROUTE2_EXECUTABLE, stderrOutput); |
| 54 | |
| 55 | throw std::runtime_error(SUDO_EXECUTABLE + " "s + IPROUTE2_EXECUTABLE + " returned non-zero exit code " + std::to_string(c.exit_code())); |
| 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 | |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 79 | auto dataFromSysrepoNoStatistics(const std::shared_ptr<sysrepo::Session>& session, const std::string& xpath, sr_datastore_t datastore) |
| 80 | { |
| 81 | auto res = dataFromSysrepo(session, xpath, datastore); |
| 82 | REQUIRE(res.erase("/statistics") == 1); |
| 83 | REQUIRE(res.erase("/statistics/in-octets") == 1); |
| 84 | REQUIRE(res.erase("/statistics/in-errors") == 1); |
| 85 | REQUIRE(res.erase("/statistics/in-discards") == 1); |
| 86 | REQUIRE(res.erase("/statistics/out-octets") == 1); |
| 87 | REQUIRE(res.erase("/statistics/out-errors") == 1); |
| 88 | REQUIRE(res.erase("/statistics/out-discards") == 1); |
| 89 | return res; |
| 90 | } |
| 91 | |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 92 | } |
| 93 | |
| 94 | TEST_CASE("Test ietf-interfaces and ietf-routing") |
| 95 | { |
| 96 | TEST_SYSREPO_INIT_LOGS; |
| 97 | TEST_SYSREPO_INIT; |
| 98 | TEST_SYSREPO_INIT_CLIENT; |
| 99 | |
| 100 | auto network = std::make_shared<velia::system::IETFInterfaces>(srSess); |
| 101 | |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 102 | 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] | 103 | |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 104 | iproute2_exec_and_wait(WAIT, "addr", "add", "192.0.2.1/24", "dev", IFACE); // from TEST-NET-1 (RFC 5737) |
| 105 | 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] | 106 | |
| 107 | std::map<std::string, std::string> initialExpected{ |
| 108 | {"/ietf-ip:ipv4", ""}, |
| 109 | {"/ietf-ip:ipv4/address[ip='192.0.2.1']", ""}, |
| 110 | {"/ietf-ip:ipv4/address[ip='192.0.2.1']/ip", "192.0.2.1"}, |
| 111 | {"/ietf-ip:ipv4/address[ip='192.0.2.1']/prefix-length", "24"}, |
| 112 | {"/ietf-ip:ipv6", ""}, |
| 113 | {"/ietf-ip:ipv6/address[ip='::ffff:192.0.2.1']", ""}, |
| 114 | {"/ietf-ip:ipv6/address[ip='::ffff:192.0.2.1']/ip", "::ffff:192.0.2.1"}, |
| 115 | {"/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] | 116 | {"/ietf-ip:ipv6/autoconf", ""}, |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 117 | {"/name", IFACE}, |
| 118 | {"/oper-status", "down"}, |
| 119 | {"/phys-address", LINK_MAC}, |
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; |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 131 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE + "']", SR_DS_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"; |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 141 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE + "']", SR_DS_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); |
| 144 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE + "']", SR_DS_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"; |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 157 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE + "']", SR_DS_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"; |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 167 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE + "']", SR_DS_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}, |
| 181 | {"/type", "iana-if-type:bridge"}, |
| 182 | }; |
| 183 | |
| 184 | iproute2_exec_and_wait(WAIT, "link", "add", "name", IFACE_BRIDGE, "address", MAC_BRIDGE, "type", "bridge"); |
| 185 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE + "']", SR_DS_OPERATIONAL) == expectedIface); |
| 186 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE_BRIDGE + "']", SR_DS_OPERATIONAL) == expectedBridge); |
| 187 | |
| 188 | iproute2_exec_and_wait(WAIT, "link", "set", "dev", IFACE, "master", IFACE_BRIDGE); |
| 189 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE + "']", SR_DS_OPERATIONAL) == expectedIface); |
| 190 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE_BRIDGE + "']", SR_DS_OPERATIONAL) == expectedBridge); |
| 191 | |
| 192 | iproute2_exec_and_wait(WAIT, "link", "set", "dev", IFACE, "up"); |
| 193 | expectedIface["/ietf-ip:ipv6/address[ip='fe80::2:2ff:fe02:202']"] = ""; |
| 194 | expectedIface["/ietf-ip:ipv6/address[ip='fe80::2:2ff:fe02:202']/ip"] = "fe80::2:2ff:fe02:202"; |
| 195 | expectedIface["/ietf-ip:ipv6/address[ip='fe80::2:2ff:fe02:202']/prefix-length"] = "64"; |
| 196 | expectedIface["/oper-status"] = "unknown"; |
| 197 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE + "']", SR_DS_OPERATIONAL) == expectedIface); |
| 198 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE_BRIDGE + "']", SR_DS_OPERATIONAL) == expectedBridge); |
| 199 | |
| 200 | iproute2_exec_and_wait(WAIT_BRIDGE, "link", "set", "dev", IFACE_BRIDGE, "up"); |
Tomáš Pecka | 9e2291b | 2021-07-14 20:39:30 +0200 | [diff] [blame] | 201 | expectedBridge["/ietf-ip:ipv6"] = ""; |
Tomáš Pecka | bd43a94 | 2021-08-04 10:12:49 +0200 | [diff] [blame] | 202 | expectedBridge["/ietf-ip:ipv6/autoconf"] = ""; |
Tomáš Pecka | 9e2291b | 2021-07-14 20:39:30 +0200 | [diff] [blame] | 203 | expectedBridge["/ietf-ip:ipv6/address[ip='fe80::22:22ff:fe22:2222']"] = ""; |
| 204 | expectedBridge["/ietf-ip:ipv6/address[ip='fe80::22:22ff:fe22:2222']/ip"] = "fe80::22:22ff:fe22:2222"; |
| 205 | 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] | 206 | expectedBridge["/oper-status"] = "up"; |
| 207 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE + "']", SR_DS_OPERATIONAL) == expectedIface); |
| 208 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE_BRIDGE + "']", SR_DS_OPERATIONAL) == expectedBridge); |
| 209 | |
| 210 | iproute2_exec_and_wait(WAIT_BRIDGE, "link", "set", "dev", IFACE_BRIDGE, "down"); |
Tomáš Pecka | 9e2291b | 2021-07-14 20:39:30 +0200 | [diff] [blame] | 211 | expectedBridge.erase("/ietf-ip:ipv6/address[ip='fe80::22:22ff:fe22:2222']"); |
| 212 | expectedBridge.erase("/ietf-ip:ipv6/address[ip='fe80::22:22ff:fe22:2222']/ip"); |
| 213 | 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] | 214 | expectedBridge["/oper-status"] = "down"; |
| 215 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE + "']", SR_DS_OPERATIONAL) == expectedIface); |
| 216 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE_BRIDGE + "']", SR_DS_OPERATIONAL) == expectedBridge); |
| 217 | |
| 218 | iproute2_exec_and_wait(WAIT, "link", "set", "dev", IFACE, "down"); |
| 219 | expectedIface.erase("/ietf-ip:ipv6/address[ip='::ffff:192.0.2.1']"); |
| 220 | expectedIface.erase("/ietf-ip:ipv6/address[ip='::ffff:192.0.2.1']/ip"); |
| 221 | expectedIface.erase("/ietf-ip:ipv6/address[ip='::ffff:192.0.2.1']/prefix-length"); |
| 222 | expectedIface.erase("/ietf-ip:ipv6/address[ip='fe80::2:2ff:fe02:202']"); |
| 223 | expectedIface.erase("/ietf-ip:ipv6/address[ip='fe80::2:2ff:fe02:202']/ip"); |
| 224 | expectedIface.erase("/ietf-ip:ipv6/address[ip='fe80::2:2ff:fe02:202']/prefix-length"); |
| 225 | expectedIface["/oper-status"] = "down"; |
| 226 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE + "']", SR_DS_OPERATIONAL) == expectedIface); |
| 227 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE_BRIDGE + "']", SR_DS_OPERATIONAL) == expectedBridge); |
| 228 | |
| 229 | iproute2_exec_and_wait(WAIT, "link", "set", "dev", IFACE, "nomaster"); |
| 230 | expectedIface.erase("/ietf-ip:ipv4/address[ip='192.0.2.1']"); |
| 231 | expectedIface.erase("/ietf-ip:ipv4/address[ip='192.0.2.1']/ip"); |
| 232 | expectedIface.erase("/ietf-ip:ipv4/address[ip='192.0.2.1']/prefix-length"); |
| 233 | expectedIface.erase("/ietf-ip:ipv4"); |
Tomáš Pecka | bd43a94 | 2021-08-04 10:12:49 +0200 | [diff] [blame] | 234 | expectedIface.erase("/ietf-ip:ipv6/autoconf"); |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 235 | expectedIface.erase("/ietf-ip:ipv6"); |
| 236 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE + "']", SR_DS_OPERATIONAL) == expectedIface); |
| 237 | REQUIRE(dataFromSysrepoNoStatistics(client, "/ietf-interfaces:interfaces/interface[name='" + IFACE_BRIDGE + "']", SR_DS_OPERATIONAL) == expectedBridge); |
| 238 | } |
| 239 | |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 240 | SECTION("Add and remove routes") |
| 241 | { |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 242 | iproute2_exec_and_wait(WAIT, "link", "set", "dev", IFACE, "up"); |
| 243 | 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] | 244 | std::this_thread::sleep_for(WAIT); |
| 245 | |
| 246 | auto data = dataFromSysrepo(client, "/ietf-routing:routing", SR_DS_OPERATIONAL); |
| 247 | REQUIRE(data["/control-plane-protocols"] == ""); |
| 248 | REQUIRE(data["/interfaces"] == ""); |
| 249 | REQUIRE(data["/ribs"] == ""); |
| 250 | |
| 251 | data = dataFromSysrepo(client, "/ietf-routing:routing/ribs/rib[name='ipv4-master']", SR_DS_OPERATIONAL); |
| 252 | REQUIRE(data["/name"] == "ipv4-master"); |
| 253 | |
| 254 | auto findRouteIndex = [&data](const std::string& prefix) { |
| 255 | std::smatch match; |
| 256 | std::regex regex(R"(route\[(\d+)\])"); |
| 257 | size_t length = 0; |
| 258 | for (const auto& [key, value] : data) { |
| 259 | if (std::regex_search(key, match, regex)) { |
| 260 | length = std::max(std::stoul(match[1]), length); |
| 261 | } |
| 262 | } |
| 263 | |
| 264 | for (size_t i = 1; i <= length; i++) { |
| 265 | const auto keyPrefix = "/routes/route["s + std::to_string(i) + "]"; |
| 266 | if (data[keyPrefix + "/ietf-ipv4-unicast-routing:destination-prefix"] == prefix) |
| 267 | return i; |
| 268 | } |
| 269 | |
| 270 | return size_t{0}; |
| 271 | }; |
| 272 | |
| 273 | { |
| 274 | auto routeIdx = findRouteIndex("198.51.100.0/24"); |
| 275 | REQUIRE(routeIdx > 0); |
| 276 | REQUIRE(data["/routes/route["s + std::to_string(routeIdx) + "]/next-hop/outgoing-interface"] == IFACE); |
| 277 | REQUIRE(data["/routes/route["s + std::to_string(routeIdx) + "]/source-protocol"] == "ietf-routing:static"); |
| 278 | } |
| 279 | { |
| 280 | auto routeIdx = findRouteIndex("192.0.2.0/24"); |
| 281 | REQUIRE(routeIdx > 0); |
| 282 | REQUIRE(data["/routes/route["s + std::to_string(routeIdx) + "]/next-hop/outgoing-interface"] == IFACE); |
| 283 | REQUIRE(data["/routes/route["s + std::to_string(routeIdx) + "]/source-protocol"] == "ietf-routing:direct"); |
| 284 | } |
| 285 | |
| 286 | data = dataFromSysrepo(client, "/ietf-routing:routing/ribs/rib[name='ipv6-master']", SR_DS_OPERATIONAL); |
| 287 | REQUIRE(data["/name"] == "ipv6-master"); |
| 288 | |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 289 | iproute2_exec_and_wait(WAIT, "route", "del", "198.51.100.0/24"); |
| 290 | iproute2_exec_and_wait(WAIT, "link", "set", IFACE, "down"); |
Tomáš Pecka | 9fa5f6a | 2021-04-13 11:36:11 +0200 | [diff] [blame] | 291 | } |
| 292 | |
Tomáš Pecka | f910893 | 2021-06-01 10:19:36 +0200 | [diff] [blame] | 293 | 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] | 294 | } |