summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/lib/cli/common_cli.c9
-rw-r--r--tests/lib/cli/common_cli.h3
-rw-r--r--tests/lib/test_plist.c48
-rw-r--r--tests/subdir.am5
-rw-r--r--tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py32
-rw-r--r--tests/topotests/bgp_community_alias/r1/bgpd.conf13
-rw-r--r--tests/topotests/bgp_community_alias/r2/bgpd.conf3
-rw-r--r--tests/topotests/bgp_community_alias/r2/zebra.conf1
-rw-r--r--tests/topotests/bgp_community_alias/test_bgp-community-alias.py44
-rw-r--r--tests/topotests/bgp_conditional_advertisement/r1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_conditional_advertisement/r2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_conditional_advertisement/r3/bgpd.conf1
-rw-r--r--tests/topotests/bgp_default_route/r2/bgpd.conf3
-rw-r--r--tests/topotests/bgp_default_route/test_bgp_default-originate.py27
-rwxr-xr-xtests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py8
-rwxr-xr-xtests/topotests/conftest.py23
-rwxr-xr-xtests/topotests/docker/frr-topotests.sh10
-rw-r--r--tests/topotests/evpn_pim_1/leaf1/pimd.conf1
-rw-r--r--tests/topotests/evpn_pim_1/leaf2/pimd.conf1
-rw-r--r--tests/topotests/evpn_pim_1/spine/pimd.conf1
-rw-r--r--tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json191
-rw-r--r--tests/topotests/lib/bgp.py412
-rw-r--r--tests/topotests/lib/common_config.py128
-rw-r--r--tests/topotests/lib/ospf.py810
-rw-r--r--tests/topotests/lib/topogen.py4
-rw-r--r--tests/topotests/lib/topotest.py63
-rw-r--r--tests/topotests/msdp_mesh_topo1/r1/pimd.conf1
-rw-r--r--tests/topotests/msdp_mesh_topo1/r2/pimd.conf1
-rw-r--r--tests/topotests/msdp_mesh_topo1/r3/pimd.conf1
-rw-r--r--tests/topotests/msdp_topo1/r1/pimd.conf1
-rw-r--r--tests/topotests/msdp_topo1/r2/pimd.conf1
-rw-r--r--tests/topotests/msdp_topo1/r3/pimd.conf1
-rw-r--r--tests/topotests/msdp_topo1/r4/pimd.conf1
-rw-r--r--tests/topotests/ospf_gr_topo1/__init__.py0
-rw-r--r--tests/topotests/ospf_gr_topo1/rt1/ospfd.conf32
-rw-r--r--tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_database.json98
-rw-r--r--tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json11
-rw-r--r--tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_route.json180
-rw-r--r--tests/topotests/ospf_gr_topo1/rt1/show_ip_route.json210
-rw-r--r--tests/topotests/ospf_gr_topo1/rt1/zebra.conf23
-rw-r--r--tests/topotests/ospf_gr_topo1/rt2/ospfd.conf37
-rw-r--r--tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_database.json160
-rw-r--r--tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json18
-rw-r--r--tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_route.json201
-rw-r--r--tests/topotests/ospf_gr_topo1/rt2/show_ip_route.json224
-rw-r--r--tests/topotests/ospf_gr_topo1/rt2/zebra.conf23
-rw-r--r--tests/topotests/ospf_gr_topo1/rt3/ospfd.conf43
-rw-r--r--tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_database.json83
-rw-r--r--tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json25
-rw-r--r--tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_route.json214
-rw-r--r--tests/topotests/ospf_gr_topo1/rt3/show_ip_route.json223
-rw-r--r--tests/topotests/ospf_gr_topo1/rt3/zebra.conf26
-rw-r--r--tests/topotests/ospf_gr_topo1/rt4/ospfd.conf37
-rw-r--r--tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_database.json164
-rw-r--r--tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json18
-rw-r--r--tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_route.json202
-rw-r--r--tests/topotests/ospf_gr_topo1/rt4/show_ip_route.json224
-rw-r--r--tests/topotests/ospf_gr_topo1/rt4/zebra.conf23
-rw-r--r--tests/topotests/ospf_gr_topo1/rt5/ospfd.conf31
-rw-r--r--tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_database.json102
-rw-r--r--tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json11
-rw-r--r--tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_route.json203
-rw-r--r--tests/topotests/ospf_gr_topo1/rt5/show_ip_route.json225
-rw-r--r--tests/topotests/ospf_gr_topo1/rt5/zebra.conf20
-rw-r--r--tests/topotests/ospf_gr_topo1/rt6/ospfd.conf38
-rw-r--r--tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_database.json168
-rw-r--r--tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json18
-rw-r--r--tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_route.json214
-rw-r--r--tests/topotests/ospf_gr_topo1/rt6/show_ip_route.json224
-rw-r--r--tests/topotests/ospf_gr_topo1/rt6/zebra.conf23
-rw-r--r--tests/topotests/ospf_gr_topo1/rt7/ospfd.conf33
-rw-r--r--tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_database.json99
-rw-r--r--tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json11
-rw-r--r--tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_route.json168
-rw-r--r--tests/topotests/ospf_gr_topo1/rt7/show_ip_route.json210
-rw-r--r--tests/topotests/ospf_gr_topo1/rt7/zebra.conf23
-rwxr-xr-xtests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py393
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp.json347
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_routemaps.json137
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py520
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py872
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py228
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py2
-rw-r--r--tests/topotests/pim_acl/h1/zebra.conf10
-rw-r--r--tests/topotests/pim_acl/h2/zebra.conf8
-rw-r--r--tests/topotests/pim_acl/r1/acl_1_pim_join.json21
-rw-r--r--tests/topotests/pim_acl/r1/acl_2_pim_join.json21
-rw-r--r--tests/topotests/pim_acl/r1/acl_3_pim_join.json21
-rw-r--r--tests/topotests/pim_acl/r1/acl_4_pim_join.json21
-rw-r--r--tests/topotests/pim_acl/r1/acl_5_pim_join.json21
-rw-r--r--tests/topotests/pim_acl/r1/acl_6_pim_join.json21
-rw-r--r--tests/topotests/pim_acl/r1/ospf_neighbor.json59
-rw-r--r--tests/topotests/pim_acl/r1/ospfd.conf16
-rw-r--r--tests/topotests/pim_acl/r1/pim_neighbor.json31
-rw-r--r--tests/topotests/pim_acl/r1/pimd.conf31
-rw-r--r--tests/topotests/pim_acl/r1/zebra.conf18
-rw-r--r--tests/topotests/pim_acl/r11/acl_1_pim_join.json19
-rw-r--r--tests/topotests/pim_acl/r11/ospfd.conf14
-rw-r--r--tests/topotests/pim_acl/r11/pimd.conf17
-rw-r--r--tests/topotests/pim_acl/r11/zebra.conf13
-rw-r--r--tests/topotests/pim_acl/r12/acl_2_pim_join.json19
-rw-r--r--tests/topotests/pim_acl/r12/ospfd.conf14
-rw-r--r--tests/topotests/pim_acl/r12/pimd.conf17
-rw-r--r--tests/topotests/pim_acl/r12/zebra.conf13
-rw-r--r--tests/topotests/pim_acl/r13/acl_3_pim_join.json19
-rw-r--r--tests/topotests/pim_acl/r13/ospfd.conf14
-rw-r--r--tests/topotests/pim_acl/r13/pimd.conf17
-rw-r--r--tests/topotests/pim_acl/r13/zebra.conf13
-rw-r--r--tests/topotests/pim_acl/r14/acl_4_pim_join.json19
-rw-r--r--tests/topotests/pim_acl/r14/acl_5_pim_join.json19
-rw-r--r--tests/topotests/pim_acl/r14/ospfd.conf14
-rw-r--r--tests/topotests/pim_acl/r14/pimd.conf18
-rw-r--r--tests/topotests/pim_acl/r14/zebra.conf13
-rw-r--r--tests/topotests/pim_acl/r15/acl_6_pim_join.json19
-rw-r--r--tests/topotests/pim_acl/r15/ospfd.conf14
-rw-r--r--tests/topotests/pim_acl/r15/pimd.conf17
-rw-r--r--tests/topotests/pim_acl/r15/zebra.conf13
-rwxr-xr-xtests/topotests/pim_acl/test_pim_acl.py418
-rw-r--r--tests/topotests/pim_basic/r1/pimd.conf1
-rw-r--r--tests/topotests/pim_basic/rp/pimd.conf1
-rw-r--r--tests/topotests/pim_basic_topo2/r2/pimd.conf1
-rw-r--r--tests/topotests/pim_igmp_vrf/h1/zebra.conf10
-rw-r--r--tests/topotests/pim_igmp_vrf/h2/zebra.conf8
-rw-r--r--tests/topotests/pim_igmp_vrf/h3/zebra.conf10
-rw-r--r--tests/topotests/pim_igmp_vrf/h4/zebra.conf8
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json15
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json16
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/ospfd.conf26
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/pim_blue_join.json22
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/pim_blue_neighbor.json13
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/pim_blue_pimreg11.json14
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/pim_red_join.json21
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/pim_red_neighbor.json13
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/pim_red_pimreg12.json14
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/pimd.conf27
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/zebra.conf30
-rw-r--r--tests/topotests/pim_igmp_vrf/r11/ospfd.conf14
-rw-r--r--tests/topotests/pim_igmp_vrf/r11/pim_blue_join.json19
-rw-r--r--tests/topotests/pim_igmp_vrf/r11/pimd.conf17
-rw-r--r--tests/topotests/pim_igmp_vrf/r11/zebra.conf13
-rw-r--r--tests/topotests/pim_igmp_vrf/r12/ospfd.conf14
-rw-r--r--tests/topotests/pim_igmp_vrf/r12/pim_red_join.json19
-rw-r--r--tests/topotests/pim_igmp_vrf/r12/pimd.conf17
-rw-r--r--tests/topotests/pim_igmp_vrf/r12/zebra.conf13
-rwxr-xr-xtests/topotests/pim_igmp_vrf/test_pim_vrf.py462
146 files changed, 9896 insertions, 664 deletions
diff --git a/tests/.gitignore b/tests/.gitignore
index 3fad1b0813..498d7dd0b7 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -36,6 +36,7 @@
/lib/test_nexthop
/lib/test_nexthop_iter
/lib/test_ntop
+/lib/test_plist
/lib/test_prefix2str
/lib/test_printfrr
/lib/test_privs
diff --git a/tests/lib/cli/common_cli.c b/tests/lib/cli/common_cli.c
index 49bc0f4fb2..8be81cc4cb 100644
--- a/tests/lib/cli/common_cli.c
+++ b/tests/lib/cli/common_cli.c
@@ -59,10 +59,13 @@ static void vty_do_exit(int isexit)
exit(0);
}
+const struct frr_yang_module_info *const *test_yang_modules = NULL;
+
/* main routine. */
int main(int argc, char **argv)
{
struct thread thread;
+ size_t yangcount;
/* Set umask before anything for security */
umask(0027);
@@ -79,7 +82,11 @@ int main(int argc, char **argv)
vty_init(master, false);
lib_cmd_init();
- nb_init(master, NULL, 0, false);
+
+ for (yangcount = 0; test_yang_modules && test_yang_modules[yangcount];
+ yangcount++)
+ ;
+ nb_init(master, test_yang_modules, yangcount, false);
test_init(argc, argv);
diff --git a/tests/lib/cli/common_cli.h b/tests/lib/cli/common_cli.h
index 15abe3b855..3042ff5b12 100644
--- a/tests/lib/cli/common_cli.h
+++ b/tests/lib/cli/common_cli.h
@@ -25,6 +25,9 @@
#include "zebra.h"
#include "vty.h"
#include "command.h"
+#include "northbound.h"
+
+extern const struct frr_yang_module_info *const *test_yang_modules;
/* function to be implemented by test */
extern void test_init(int argc, char **argv);
diff --git a/tests/lib/test_plist.c b/tests/lib/test_plist.c
new file mode 100644
index 0000000000..ee7a9ebf30
--- /dev/null
+++ b/tests/lib/test_plist.c
@@ -0,0 +1,48 @@
+/*
+ * Simple prefix list querying tool
+ *
+ * Copyright (C) 2021 by David Lamparter,
+ * for Open Source Routing / NetDEF, Inc.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "lib/plist.h"
+#include "lib/filter.h"
+#include "tests/lib/cli/common_cli.h"
+
+static const struct frr_yang_module_info *const my_yang_modules[] = {
+ &frr_filter_info,
+ NULL,
+};
+
+__attribute__((_CONSTRUCTOR(2000)))
+static void test_yang_modules_set(void)
+{
+ test_yang_modules = my_yang_modules;
+}
+
+void test_init(int argc, char **argv)
+{
+ prefix_list_init();
+ filter_cli_init();
+
+ /* nothing else to do here, giving stand-alone access to the prefix
+ * list code's "debug prefix-list ..." command is the only purpose of
+ * this "test".
+ */
+}
diff --git a/tests/subdir.am b/tests/subdir.am
index c2153140f5..86c1aa4284 100644
--- a/tests/subdir.am
+++ b/tests/subdir.am
@@ -87,6 +87,7 @@ check_PROGRAMS = \
tests/lib/test_nexthop_iter \
tests/lib/test_nexthop \
tests/lib/test_ntop \
+ tests/lib/test_plist \
tests/lib/test_prefix2str \
tests/lib/test_printfrr \
tests/lib/test_privs \
@@ -344,6 +345,10 @@ tests_lib_test_ntop_CFLAGS = $(TESTS_CFLAGS)
tests_lib_test_ntop_CPPFLAGS = $(CPPFLAGS_BASE) # no assert override
tests_lib_test_ntop_LDADD = # none
tests_lib_test_ntop_SOURCES = tests/lib/test_ntop.c tests/helpers/c/prng.c
+tests_lib_test_plist_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_plist_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_plist_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_plist_SOURCES = tests/lib/test_plist.c tests/lib/cli/common_cli.c
tests_lib_test_prefix2str_CFLAGS = $(TESTS_CFLAGS)
tests_lib_test_prefix2str_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_test_prefix2str_LDADD = $(ALL_TESTS_LDADD)
diff --git a/tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py b/tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py
index 374cce21f6..485a76c6b2 100644
--- a/tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py
+++ b/tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py
@@ -774,9 +774,9 @@ def test_BGP_attributes_with_vrf_default_keyword_p0(request):
}
result = verify_bgp_rib(tgen, addr_type, dut, input_dict)
- assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
result = verify_rib(tgen, addr_type, dut, input_dict)
- assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
for addr_type in ADDR_TYPES:
dut = "r4"
@@ -793,9 +793,9 @@ def test_BGP_attributes_with_vrf_default_keyword_p0(request):
}
result = verify_bgp_rib(tgen, addr_type, dut, input_dict)
- assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
result = verify_rib(tgen, addr_type, dut, input_dict)
- assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
input_dict_4 = {"largeCommunity": "500:500:500", "community": "500:500"}
@@ -1134,15 +1134,10 @@ def test_bgp_with_loopback_with_same_subnet_p1(request):
dut = "r1"
protocol = "bgp"
for addr_type in ADDR_TYPES:
- result = verify_rib(tgen, addr_type, dut, input_dict_r1, protocol=protocol)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- result = verify_fib_routes(tgen, addr_type, dut, input_dict_r1, expected=False)
- assert result is not True, "Testcase {} : Failed \n"
+ result = verify_fib_routes(tgen, addr_type, dut, input_dict_r1)
+ assert result is not True, "Testcase {} : Failed \n".format(tc_name)
"Expected behavior: routes should not present in fib \n"
- "Error: {}".format(tc_name, result)
+ "Error: {}".format(result)
step("Verify Ipv4 and Ipv6 network installed in r3 RIB but not in FIB")
input_dict_r3 = {
@@ -1156,17 +1151,10 @@ def test_bgp_with_loopback_with_same_subnet_p1(request):
dut = "r3"
protocol = "bgp"
for addr_type in ADDR_TYPES:
- result = verify_rib(
- tgen, addr_type, dut, input_dict_r3, protocol=protocol, fib=None
- )
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- result = verify_fib_routes(tgen, addr_type, dut, input_dict_r1, expected=False)
- assert result is not True, "Testcase {} : Failed \n"
+ result = verify_fib_routes(tgen, addr_type, dut, input_dict_r1)
+ assert result is not True, "Testcase {} : Failed \n".format(tc_name)
"Expected behavior: routes should not present in fib \n"
- "Error: {}".format(tc_name, result)
+ "Error: {}".format(result)
write_test_footer(tc_name)
diff --git a/tests/topotests/bgp_community_alias/r1/bgpd.conf b/tests/topotests/bgp_community_alias/r1/bgpd.conf
index 06113bdd2a..a6366204e8 100644
--- a/tests/topotests/bgp_community_alias/r1/bgpd.conf
+++ b/tests/topotests/bgp_community_alias/r1/bgpd.conf
@@ -6,4 +6,17 @@ bgp community alias 65001:1:1 large-community-r2-1
router bgp 65001
no bgp ebgp-requires-policy
neighbor 192.168.1.2 remote-as external
+ address-family ipv4 unicast
+ redistribute connected
+ neighbor 192.168.1.2 route-map r2 in
+ exit-address-family
+!
+route-map r2 permit 10
+ match alias community-r2-1
+ set tag 10
+route-map r2 permit 20
+ match alias community-r2-2
+ set tag 20
+route-map r2 permit 30
+ set tag 100
!
diff --git a/tests/topotests/bgp_community_alias/r2/bgpd.conf b/tests/topotests/bgp_community_alias/r2/bgpd.conf
index fc67ff2ad2..9276fe592d 100644
--- a/tests/topotests/bgp_community_alias/r2/bgpd.conf
+++ b/tests/topotests/bgp_community_alias/r2/bgpd.conf
@@ -8,6 +8,7 @@ router bgp 65002
!
ip prefix-list p1 permit 172.16.16.1/32
ip prefix-list p2 permit 172.16.16.2/32
+ip prefix-list p3 permit 172.16.16.3/32
!
route-map r1 permit 10
match ip address prefix-list p1
@@ -16,4 +17,6 @@ route-map r1 permit 10
route-map r1 permit 20
match ip address prefix-list p2
set community 65002:1 65002:2
+route-map r1 permit 30
+ match ip address prefix-list p3
!
diff --git a/tests/topotests/bgp_community_alias/r2/zebra.conf b/tests/topotests/bgp_community_alias/r2/zebra.conf
index a806628a8e..b8cb9baf3c 100644
--- a/tests/topotests/bgp_community_alias/r2/zebra.conf
+++ b/tests/topotests/bgp_community_alias/r2/zebra.conf
@@ -2,6 +2,7 @@
int lo
ip address 172.16.16.1/32
ip address 172.16.16.2/32
+ ip address 172.16.16.3/32
!
int r2-eth0
ip address 192.168.1.2/24
diff --git a/tests/topotests/bgp_community_alias/test_bgp-community-alias.py b/tests/topotests/bgp_community_alias/test_bgp-community-alias.py
index 90eeaaa731..c41ba810f1 100644
--- a/tests/topotests/bgp_community_alias/test_bgp-community-alias.py
+++ b/tests/topotests/bgp_community_alias/test_bgp-community-alias.py
@@ -84,39 +84,57 @@ def test_bgp_community_alias():
router = tgen.gears["r1"]
def _bgp_converge(router):
- output = json.loads(
- router.vtysh_cmd("show bgp ipv4 unicast 172.16.16.1/32 json")
- )
+ output = json.loads(router.vtysh_cmd("show ip route json"))
expected = {
- "paths": [
+ "172.16.16.1/32": [
+ {
+ "tag": 10,
+ "communities": "community-r2-1 65001:2",
+ "largeCommunities": "large-community-r2-1 65001:1:2",
+ }
+ ],
+ "172.16.16.2/32": [
+ {
+ "tag": 20,
+ "communities": "65002:1 community-r2-2",
+ "largeCommunities": "",
+ }
+ ],
+ "172.16.16.3/32": [
{
- "community": {"string": "community-r2-1 65001:2"},
- "largeCommunity": {"string": "large-community-r2-1 65001:1:2"},
+ "tag": 100,
+ "communities": "",
+ "largeCommunities": "",
}
- ]
+ ],
}
return topotest.json_cmp(output, expected)
test_func = functools.partial(_bgp_converge, router)
success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
- assert result is None, 'Cannot see BGP community aliases "{}"'.format(router)
+ assert result is None, "Cannot see BGP community aliases at r1"
def _bgp_show_prefixes_by_alias(router):
output = json.loads(
- router.vtysh_cmd("show bgp ipv4 unicast alias community-r2-2 json detail")
+ router.vtysh_cmd(
+ "show bgp ipv4 unicast alias large-community-r2-1 json detail"
+ )
)
expected = {
"routes": {
- "172.16.16.2/32": [{"community": {"string": "65002:1 community-r2-2"}}]
+ "172.16.16.1/32": [
+ {
+ "community": {"string": "community-r2-1 65001:2"},
+ "largeCommunity": {"string": "large-community-r2-1 65001:1:2"},
+ }
+ ]
}
}
return topotest.json_cmp(output, expected)
test_func = functools.partial(_bgp_show_prefixes_by_alias, router)
success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
- assert result is None, 'Cannot see BGP prefixes by community alias "{}"'.format(
- router
- )
+ assert result is None, "Cannot see BGP prefixes by community alias at r1"
if __name__ == "__main__":
diff --git a/tests/topotests/bgp_conditional_advertisement/r1/bgpd.conf b/tests/topotests/bgp_conditional_advertisement/r1/bgpd.conf
index 633d1832fd..293b38c7e8 100644
--- a/tests/topotests/bgp_conditional_advertisement/r1/bgpd.conf
+++ b/tests/topotests/bgp_conditional_advertisement/r1/bgpd.conf
@@ -17,6 +17,7 @@ route-map DEF permit 10
!
router bgp 1
bgp log-neighbor-changes
+ bgp conditional-advertisement timer 5
no bgp ebgp-requires-policy
neighbor 10.10.10.2 remote-as 2
!
diff --git a/tests/topotests/bgp_conditional_advertisement/r2/bgpd.conf b/tests/topotests/bgp_conditional_advertisement/r2/bgpd.conf
index c6147fe658..82525fac64 100644
--- a/tests/topotests/bgp_conditional_advertisement/r2/bgpd.conf
+++ b/tests/topotests/bgp_conditional_advertisement/r2/bgpd.conf
@@ -32,6 +32,7 @@ route-map RMAP-2 deny 10
!
router bgp 2
bgp log-neighbor-changes
+ bgp conditional-advertisement timer 5
no bgp ebgp-requires-policy
neighbor 10.10.10.1 remote-as 1
neighbor 10.10.20.3 remote-as 3
diff --git a/tests/topotests/bgp_conditional_advertisement/r3/bgpd.conf b/tests/topotests/bgp_conditional_advertisement/r3/bgpd.conf
index 2f4f5068d8..f389f309a6 100644
--- a/tests/topotests/bgp_conditional_advertisement/r3/bgpd.conf
+++ b/tests/topotests/bgp_conditional_advertisement/r3/bgpd.conf
@@ -1,6 +1,7 @@
!
router bgp 3
bgp log-neighbor-changes
+ bgp conditional-advertisement timer 5
no bgp ebgp-requires-policy
neighbor 10.10.20.2 remote-as 2
!
diff --git a/tests/topotests/bgp_default_route/r2/bgpd.conf b/tests/topotests/bgp_default_route/r2/bgpd.conf
index 00c96cc58b..6d1080c119 100644
--- a/tests/topotests/bgp_default_route/r2/bgpd.conf
+++ b/tests/topotests/bgp_default_route/r2/bgpd.conf
@@ -2,7 +2,4 @@ router bgp 65001
no bgp ebgp-requires-policy
neighbor 192.168.255.1 remote-as 65000
neighbor 192.168.255.1 timers 3 10
- address-family ipv4 unicast
- redistribute connected
- exit-address-family
!
diff --git a/tests/topotests/bgp_default_route/test_bgp_default-originate.py b/tests/topotests/bgp_default_route/test_bgp_default-originate.py
index d8de0f0ac6..19632162b4 100644
--- a/tests/topotests/bgp_default_route/test_bgp_default-originate.py
+++ b/tests/topotests/bgp_default_route/test_bgp_default-originate.py
@@ -79,10 +79,10 @@ def test_bgp_default_originate_route_map():
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
- router = tgen.gears["r2"]
-
- def _bgp_converge(router):
- output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+ def _bgp_check_if_received():
+ output = json.loads(
+ tgen.gears["r2"].vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")
+ )
expected = {
"192.168.255.1": {
"bgpState": "Established",
@@ -91,22 +91,27 @@ def test_bgp_default_originate_route_map():
}
return topotest.json_cmp(output, expected)
+ def _bgp_check_if_originated():
+ output = json.loads(tgen.gears["r1"].vtysh_cmd("show ip bgp summary json"))
+ expected = {"ipv4Unicast": {"peers": {"192.168.255.2": {"pfxSnt": 1}}}}
+ return topotest.json_cmp(output, expected)
+
def _bgp_default_route_is_valid(router):
output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json"))
expected = {"paths": [{"valid": True}]}
return topotest.json_cmp(output, expected)
- test_func = functools.partial(_bgp_converge, router)
+ test_func = functools.partial(_bgp_check_if_received)
success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "No 0.0.0.0/0 at r2 from r1"
- assert result is None, 'Failed to see bgp convergence in "{}"'.format(router)
-
- test_func = functools.partial(_bgp_default_route_is_valid, router)
+ test_func = functools.partial(_bgp_check_if_originated)
success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "No 0.0.0.0/0 from r1 to r2"
- assert (
- result is None
- ), 'Failed to see applied metric for default route in "{}"'.format(router)
+ test_func = functools.partial(_bgp_default_route_is_valid, tgen.gears["r2"])
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "Failed to see 0.0.0.0/0 in r2"
if __name__ == "__main__":
diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py b/tests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py
index 086bad6481..fd5bb38b98 100755
--- a/tests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py
@@ -365,6 +365,10 @@ def test_ip_pe1_learn():
"run the IP learn test for PE1"
tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
host1 = tgen.gears["host1"]
pe1 = tgen.gears["PE1"]
pe2 = tgen.gears["PE2"]
@@ -380,6 +384,10 @@ def test_ip_pe2_learn():
"run the IP learn test for PE2"
tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
host2 = tgen.gears["host2"]
pe1 = tgen.gears["PE1"]
pe2 = tgen.gears["PE2"]
diff --git a/tests/topotests/conftest.py b/tests/topotests/conftest.py
index e57db7471c..d119b0931b 100755
--- a/tests/topotests/conftest.py
+++ b/tests/topotests/conftest.py
@@ -26,6 +26,12 @@ def pytest_addoption(parser):
only run the setup_module() to setup the topology without running any tests.
"""
parser.addoption(
+ "--asan-abort",
+ action="store_true",
+ help="Configure address sanitizer to abort process on error",
+ )
+
+ parser.addoption(
"--gdb-breakpoints",
metavar="SYMBOL[,SYMBOL...]",
help="Comma-separated list of functions to set gdb breakpoints on",
@@ -68,6 +74,12 @@ def pytest_addoption(parser):
)
parser.addoption(
+ "--strace-daemons",
+ metavar="DAEMON[,DAEMON...]",
+ help="Comma-separated list of daemons to strace, or 'all'",
+ )
+
+ parser.addoption(
"--topology-only",
action="store_true",
default=False,
@@ -167,6 +179,9 @@ def pytest_configure(config):
if not diagnose_env():
pytest.exit("environment has errors, please read the logs")
+ asan_abort = config.getoption("--asan-abort")
+ topotest_extra_config["asan_abort"] = asan_abort
+
gdb_routers = config.getoption("--gdb-routers")
gdb_routers = gdb_routers.split(",") if gdb_routers else []
topotest_extra_config["gdb_routers"] = gdb_routers
@@ -185,6 +200,9 @@ def pytest_configure(config):
shell = config.getoption("--shell")
topotest_extra_config["shell"] = shell.split(",") if shell else []
+ strace = config.getoption("--strace-daemons")
+ topotest_extra_config["strace_daemons"] = strace.split(",") if strace else []
+
pause_after = config.getoption("--pause-after")
shell_on_error = config.getoption("--shell-on-error")
@@ -244,6 +262,11 @@ def pytest_runtest_makereport(item, call):
)
)
+ # We want to pause, if requested, on any error not just test cases
+ # (e.g., call.when == "setup")
+ if not pause:
+ pause = topotest_extra_config["pause_after"]
+
# (topogen) Set topology error to avoid advancing in the test.
tgen = get_topogen()
if tgen is not None:
diff --git a/tests/topotests/docker/frr-topotests.sh b/tests/topotests/docker/frr-topotests.sh
index 9ef59b3bbc..1eaaea2971 100755
--- a/tests/topotests/docker/frr-topotests.sh
+++ b/tests/topotests/docker/frr-topotests.sh
@@ -145,7 +145,15 @@ if [ "${TOPOTEST_PULL:-1}" = "1" ]; then
docker pull frrouting/topotests:latest
fi
+if [[ -n "$TMUX" ]]; then
+ TMUX_OPTIONS="-v $(dirname $TMUX):$(dirname $TMUX) -e TMUX=$TMUX -e TMUX_PANE=$TMUX_PANE"
+fi
+
+if [[ -n "$STY" ]]; then
+ SCREEN_OPTIONS="-v /run/screen:/run/screen -e STY=$STY"
+fi
set -- --rm -i \
+ -v "$HOME:$HOME:ro" \
-v "$TOPOTEST_LOGS:/tmp" \
-v "$TOPOTEST_FRR:/root/host-frr:ro" \
-v "$TOPOTEST_BUILDCACHE:/root/persist" \
@@ -154,6 +162,8 @@ set -- --rm -i \
-e "TOPOTEST_DOC=$TOPOTEST_DOC" \
-e "TOPOTEST_SANITIZER=$TOPOTEST_SANITIZER" \
--privileged \
+ $SCREEN_OPTINS \
+ $TMUX_OPTIONS \
$TOPOTEST_OPTIONS \
frrouting/topotests:latest "$@"
diff --git a/tests/topotests/evpn_pim_1/leaf1/pimd.conf b/tests/topotests/evpn_pim_1/leaf1/pimd.conf
index 293e252086..d85f33d1fc 100644
--- a/tests/topotests/evpn_pim_1/leaf1/pimd.conf
+++ b/tests/topotests/evpn_pim_1/leaf1/pimd.conf
@@ -2,6 +2,7 @@ debug pim events
debug pim nht
debug pim zebra
ip pim rp 192.168.100.1
+ip pim join-prune-interval 5
!
int lo
ip pim
diff --git a/tests/topotests/evpn_pim_1/leaf2/pimd.conf b/tests/topotests/evpn_pim_1/leaf2/pimd.conf
index 08d5a19a2a..d775b800b3 100644
--- a/tests/topotests/evpn_pim_1/leaf2/pimd.conf
+++ b/tests/topotests/evpn_pim_1/leaf2/pimd.conf
@@ -1,4 +1,5 @@
ip pim rp 192.168.100.1
+ip pim join-prune-interval 5
!
int lo
ip pim
diff --git a/tests/topotests/evpn_pim_1/spine/pimd.conf b/tests/topotests/evpn_pim_1/spine/pimd.conf
index 56adda5cc4..12c6d6f85c 100644
--- a/tests/topotests/evpn_pim_1/spine/pimd.conf
+++ b/tests/topotests/evpn_pim_1/spine/pimd.conf
@@ -1,4 +1,5 @@
ip pim rp 192.168.100.1
+ip pim join-prune-interval 5
!
int lo
ip pim
diff --git a/tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json b/tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json
index 14842da326..dd412708bb 100644
--- a/tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json
+++ b/tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json
@@ -41,7 +41,10 @@
"neighbor": {
"e1": {
"dest_link": {
- "r1": {}
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -55,7 +58,10 @@
"neighbor": {
"e1": {
"dest_link": {
- "r1": {}
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -106,7 +112,10 @@
"neighbor": {
"e1": {
"dest_link": {
- "r2-link1": {}
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -120,7 +129,10 @@
"neighbor": {
"e1": {
"dest_link": {
- "r2-link1": {}
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -140,7 +152,10 @@
"neighbor": {
"e1": {
"dest_link": {
- "r2-link2": {}
+ "r2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -154,7 +169,10 @@
"neighbor": {
"e1": {
"dest_link": {
- "r2-link2": {}
+ "r2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -222,7 +240,10 @@
"neighbor": {
"r1": {
"dest_link": {
- "e1": {}
+ "e1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -233,7 +254,10 @@
"neighbor": {
"r1": {
"dest_link": {
- "e1": {}
+ "e1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -262,7 +286,10 @@
"neighbor": {
"r2": {
"dest_link": {
- "e1-link1": {}
+ "e1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -273,7 +300,10 @@
"neighbor": {
"r2": {
"dest_link": {
- "e1-link1": {}
+ "e1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -302,7 +332,10 @@
"neighbor": {
"r2": {
"dest_link": {
- "e1-link2": {}
+ "e1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -313,7 +346,10 @@
"neighbor": {
"r2": {
"dest_link": {
- "e1-link2": {}
+ "e1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -342,6 +378,8 @@
"d1": {
"dest_link": {
"e1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
"deactivate": "ipv4"
}
}
@@ -349,6 +387,8 @@
"d2": {
"dest_link": {
"e1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
"deactivate": "ipv4"
}
}
@@ -412,6 +452,8 @@
"e1": {
"dest_link": {
"d1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
"deactivate": "ipv4"
}
}
@@ -442,7 +484,10 @@
"neighbor": {
"r3": {
"dest_link": {
- "d1": {}
+ "d1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -453,7 +498,10 @@
"neighbor": {
"r3": {
"dest_link": {
- "d1": {}
+ "d1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -482,7 +530,10 @@
"neighbor": {
"r4": {
"dest_link": {
- "d1-link1": {}
+ "d1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -493,7 +544,10 @@
"neighbor": {
"r4": {
"dest_link": {
- "d1-link1": {}
+ "d1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -522,7 +576,10 @@
"neighbor": {
"r4": {
"dest_link": {
- "d1-link2": {}
+ "d1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -533,7 +590,10 @@
"neighbor": {
"r4": {
"dest_link": {
- "d1-link2": {}
+ "d1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -590,7 +650,9 @@
"e1": {
"dest_link": {
"d2-link1": {
- "deactivate": "ipv4"
+ "deactivate": "ipv4",
+ "keepalivetimer": 1,
+ "holddowntimer": 3
}
}
}
@@ -620,7 +682,10 @@
"neighbor": {
"r3": {
"dest_link": {
- "d2": {}
+ "d2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -631,7 +696,10 @@
"neighbor": {
"r3": {
"dest_link": {
- "d2": {}
+ "d2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -660,7 +728,10 @@
"neighbor": {
"r4": {
"dest_link": {
- "d2-link1": {}
+ "d2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -671,7 +742,10 @@
"neighbor": {
"r4": {
"dest_link": {
- "d2-link1": {}
+ "d2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -700,7 +774,10 @@
"neighbor": {
"r4": {
"dest_link": {
- "d2-link2": {}
+ "d2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -711,7 +788,10 @@
"neighbor": {
"r4": {
"dest_link": {
- "d2-link2": {}
+ "d2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -755,12 +835,18 @@
"neighbor": {
"d1": {
"dest_link": {
- "r3": {}
+ "r3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
},
"d2": {
"dest_link": {
- "r3": {}
+ "r3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -771,12 +857,18 @@
"neighbor": {
"d1": {
"dest_link": {
- "r3": {}
+ "r3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
},
"d2": {
"dest_link": {
- "r3": {}
+ "r3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -814,12 +906,18 @@
"neighbor": {
"d1": {
"dest_link": {
- "r4-link1": {}
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
},
"d2": {
"dest_link": {
- "r4-link1": {}
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -830,12 +928,18 @@
"neighbor": {
"d1": {
"dest_link": {
- "r4-link1": {}
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
},
"d2": {
"dest_link": {
- "r4-link1": {}
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -852,12 +956,18 @@
"neighbor": {
"d1": {
"dest_link": {
- "r4-link2": {}
+ "r4-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
},
"d2": {
"dest_link": {
- "r4-link2": {}
+ "r4-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -868,12 +978,18 @@
"neighbor": {
"d1": {
"dest_link": {
- "r4-link2": {}
+ "r4-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
},
"d2": {
"dest_link": {
- "r4-link2": {}
+ "r4-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
}
}
}
@@ -885,3 +1001,4 @@
}
}
}
+
diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py
index a236a916b5..2f1f67439f 100644
--- a/tests/topotests/lib/bgp.py
+++ b/tests/topotests/lib/bgp.py
@@ -44,6 +44,7 @@ from lib.common_config import (
FRRCFG_FILE,
retry,
get_ipv6_linklocal_address,
+ get_frr_ipv6_linklocal
)
LOGDIR = "/tmp/topotests/"
@@ -265,6 +266,11 @@ def __create_bgp_global(tgen, input_dict, router, build=False):
config_data.append("bgp router-id {}".format(router_id))
config_data.append("no bgp network import-check")
+ bgp_peer_grp_data = bgp_data.setdefault("peer-group", {})
+
+ if "peer-group" in bgp_data and bgp_peer_grp_data:
+ peer_grp_data = __create_bgp_peer_group(tgen, bgp_peer_grp_data, router)
+ config_data.extend(peer_grp_data)
bst_path = bgp_data.setdefault("bestpath", None)
if bst_path:
@@ -380,6 +386,7 @@ def __create_bgp_unicast_neighbor(
addr_data = addr_dict["unicast"]
if addr_data:
config_data.append("address-family {} unicast".format(addr_type))
+
advertise_network = addr_data.setdefault("advertise_networks", [])
for advertise_network_dict in advertise_network:
network = advertise_network_dict["network"]
@@ -404,14 +411,29 @@ def __create_bgp_unicast_neighbor(
config_data.append(cmd)
+ import_cmd = addr_data.setdefault("import", {})
+ if import_cmd:
+ try:
+ if import_cmd["delete"]:
+ config_data.append("no import vrf {}".format(import_cmd["vrf"]))
+ except KeyError:
+ config_data.append("import vrf {}".format(import_cmd["vrf"]))
+
max_paths = addr_data.setdefault("maximum_paths", {})
if max_paths:
ibgp = max_paths.setdefault("ibgp", None)
ebgp = max_paths.setdefault("ebgp", None)
+ del_cmd = max_paths.setdefault("delete", False)
if ibgp:
- config_data.append("maximum-paths ibgp {}".format(ibgp))
+ if del_cmd:
+ config_data.append("no maximum-paths ibgp {}".format(ibgp))
+ else:
+ config_data.append("maximum-paths ibgp {}".format(ibgp))
if ebgp:
- config_data.append("maximum-paths {}".format(ebgp))
+ if del_cmd:
+ config_data.append("no maximum-paths {}".format(ebgp))
+ else:
+ config_data.append("maximum-paths {}".format(ebgp))
aggregate_addresses = addr_data.setdefault("aggregate_address", [])
for aggregate_address in aggregate_addresses:
@@ -649,6 +671,38 @@ def __create_l2vpn_evpn_address_family(
return config_data
+def __create_bgp_peer_group(topo, input_dict, router):
+ """
+ Helper API to create neighbor specific configuration
+
+ Parameters
+ ----------
+ * `topo` : json file data
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `router` : router id to be configured
+ """
+ config_data = []
+ logger.debug("Entering lib API: __create_bgp_peer_group()")
+
+ for grp, grp_dict in input_dict.items():
+ config_data.append("neighbor {} peer-group".format(grp))
+ neigh_cxt = "neighbor {} ".format(grp)
+ update_source = grp_dict.setdefault("update-source", None)
+ remote_as = grp_dict.setdefault("remote-as", None)
+ capability = grp_dict.setdefault("capability", None)
+ if update_source:
+ config_data.append("{} update-source {}".format(neigh_cxt, update_source))
+
+ if remote_as:
+ config_data.append("{} remote-as {}".format(neigh_cxt, remote_as))
+
+ if capability:
+ config_data.append("{} capability {}".format(neigh_cxt, capability))
+
+ logger.debug("Exiting lib API: __create_bgp_peer_group()")
+ return config_data
+
+
def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True):
"""
Helper API to create neighbor specific configuration
@@ -660,10 +714,9 @@ def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True):
* `input_dict` : Input dict data, required when configuring from testcase
* `router` : router id to be configured
"""
-
config_data = []
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
-
+ tgen = get_topogen()
bgp_data = input_dict["address_family"]
neigh_data = bgp_data[addr_type]["unicast"]["neighbor"]
@@ -672,35 +725,91 @@ def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True):
nh_details = topo[name]
if "vrfs" in topo[router] or type(nh_details["bgp"]) is list:
- remote_as = nh_details["bgp"][0]["local_as"]
+ for vrf_data in nh_details["bgp"]:
+ if "vrf" in nh_details["links"][dest_link] and "vrf" in vrf_data:
+ if nh_details["links"][dest_link]["vrf"] == vrf_data["vrf"]:
+ remote_as = vrf_data["local_as"]
+ break
+ else:
+ if "vrf" not in vrf_data:
+ remote_as = vrf_data["local_as"]
+ break
+
else:
remote_as = nh_details["bgp"]["local_as"]
update_source = None
- if dest_link in nh_details["links"].keys():
- ip_addr = nh_details["links"][dest_link][addr_type].split("/")[0]
- # Loopback interface
- if "source_link" in peer and peer["source_link"] == "lo":
- update_source = topo[router]["links"]["lo"][addr_type].split("/")[0]
+ if "neighbor_type" in peer and peer["neighbor_type"] == "unnumbered":
+ ip_addr = nh_details["links"][dest_link]["peer-interface"]
+ elif "neighbor_type" in peer and peer["neighbor_type"] == "link-local":
+ intf = topo[name]["links"][dest_link]["interface"]
+ ip_addr = get_frr_ipv6_linklocal(tgen, name, intf)
+ elif dest_link in nh_details["links"].keys():
+ try:
+ ip_addr = nh_details["links"][dest_link][addr_type].split("/")[0]
+ except KeyError:
+ intf = topo[name]["links"][dest_link]["interface"]
+ ip_addr = get_frr_ipv6_linklocal(tgen, name, intf)
+ if "delete" in peer and peer["delete"]:
+ neigh_cxt = "no neighbor {}".format(ip_addr)
+ config_data.append("{}".format(neigh_cxt))
+ return config_data
+ else:
+ neigh_cxt = "neighbor {}".format(ip_addr)
- neigh_cxt = "neighbor {}".format(ip_addr)
+ if "peer-group" in peer:
+ config_data.append(
+ "neighbor {} interface peer-group {}".format(
+ ip_addr, peer["peer-group"]
+ )
+ )
+
+ # Loopback interface
+ if "source_link" in peer:
+ if peer["source_link"] == "lo":
+ update_source = topo[router]["links"]["lo"][addr_type].split("/")[0]
+ else:
+ update_source = topo[router]["links"][peer["source_link"]][
+ "interface"
+ ]
+ if "peer-group" not in peer:
+ if "neighbor_type" in peer and peer["neighbor_type"] == "unnumbered":
+ config_data.append(
+ "{} interface remote-as {}".format(neigh_cxt, remote_as)
+ )
+ elif add_neigh:
+ config_data.append("{} remote-as {}".format(neigh_cxt, remote_as))
- if add_neigh:
- config_data.append("{} remote-as {}".format(neigh_cxt, remote_as))
if addr_type == "ipv6":
config_data.append("address-family ipv6 unicast")
config_data.append("{} activate".format(neigh_cxt))
+ if "neighbor_type" in peer and peer["neighbor_type"] == "link-local":
+ config_data.append(
+ "{} update-source {}".format(
+ neigh_cxt, nh_details["links"][dest_link]["peer-interface"]
+ )
+ )
+ config_data.append(
+ "{} interface {}".format(
+ neigh_cxt, nh_details["links"][dest_link]["peer-interface"]
+ )
+ )
+
disable_connected = peer.setdefault("disable_connected_check", False)
keep_alive = peer.setdefault("keepalivetimer", 3)
hold_down = peer.setdefault("holddowntimer", 10)
password = peer.setdefault("password", None)
no_password = peer.setdefault("no_password", None)
+ capability = peer.setdefault("capability", None)
max_hop_limit = peer.setdefault("ebgp_multihop", 1)
+
graceful_restart = peer.setdefault("graceful-restart", None)
graceful_restart_helper = peer.setdefault("graceful-restart-helper", None)
graceful_restart_disable = peer.setdefault("graceful-restart-disable", None)
+ if capability:
+ config_data.append("{} capability {}".format(neigh_cxt, capability))
if update_source:
config_data.append(
@@ -718,7 +827,6 @@ def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True):
config_data.append(
"{} timers {} {}".format(neigh_cxt, keep_alive, hold_down)
)
-
if graceful_restart:
config_data.append("{} graceful-restart".format(neigh_cxt))
elif graceful_restart == False:
@@ -768,7 +876,7 @@ def __create_bgp_unicast_address_family(
config_data = []
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
-
+ tgen = get_topogen()
bgp_data = input_dict["address_family"]
neigh_data = bgp_data[addr_type]["unicast"]["neighbor"]
@@ -784,16 +892,34 @@ def __create_bgp_unicast_address_family(
for destRouterLink, data in sorted(nh_details["links"].items()):
if "type" in data and data["type"] == "loopback":
if dest_link == destRouterLink:
- ip_addr = nh_details["links"][destRouterLink][
- addr_type
- ].split("/")[0]
+ ip_addr = (
+ nh_details["links"][destRouterLink][addr_type]
+ .split("/")[0]
+ .lower()
+ )
# Physical interface
else:
- if dest_link in nh_details["links"].keys():
-
- ip_addr = nh_details["links"][dest_link][addr_type].split("/")[0]
- if addr_type == "ipv4" and bgp_data["ipv6"]:
+ # check the neighbor type if un numbered nbr, use interface.
+ if "neighbor_type" in peer and peer["neighbor_type"] == "unnumbered":
+ ip_addr = nh_details["links"][dest_link]["peer-interface"]
+ elif "neighbor_type" in peer and peer["neighbor_type"] == "link-local":
+ intf = topo[peer_name]["links"][dest_link]["interface"]
+ ip_addr = get_frr_ipv6_linklocal(tgen, peer_name, intf)
+ elif dest_link in nh_details["links"].keys():
+ try:
+ ip_addr = nh_details["links"][dest_link][addr_type].split("/")[
+ 0
+ ]
+ except KeyError:
+ intf = topo[peer_name]["links"][dest_link]["interface"]
+ ip_addr = get_frr_ipv6_linklocal(tgen, peer_name, intf)
+ if (
+ addr_type == "ipv4"
+ and bgp_data["ipv6"]
+ and check_address_types("ipv6")
+ and "ipv6" in nh_details["links"][dest_link]
+ ):
deactivate = nh_details["links"][dest_link]["ipv6"].split("/")[
0
]
@@ -822,6 +948,7 @@ def __create_bgp_unicast_address_family(
prefix_lists = peer.setdefault("prefix_lists", {})
route_maps = peer.setdefault("route_maps", {})
no_send_community = peer.setdefault("no_send_community", None)
+ capability = peer.setdefault("capability", None)
allowas_in = peer.setdefault("allowas-in", None)
# next-hop-self
@@ -841,6 +968,11 @@ def __create_bgp_unicast_address_family(
"no {} send-community {}".format(neigh_cxt, no_send_community)
)
+ # capability_ext_nh
+ if capability and addr_type == "ipv6":
+ config_data.append("address-family ipv4 unicast")
+ config_data.append("{} activate".format(neigh_cxt))
+
if "allowas_in" in peer:
allow_as_in = peer["allowas_in"]
config_data.append("{} allowas-in {}".format(neigh_cxt, allow_as_in))
@@ -1067,33 +1199,37 @@ def verify_bgp_convergence(tgen, topo, dut=None, expected=True):
API will verify if BGP is converged with in the given time frame.
Running "show bgp summary json" command and verify bgp neighbor
state is established,
+
Parameters
----------
* `tgen`: topogen object
* `topo`: input json file data
* `dut`: device under test
- * `expected` : expected results from API, by-default True
Usage
-----
# To veriry is BGP is converged for all the routers used in
topology
results = verify_bgp_convergence(tgen, topo, dut="r1")
+
Returns
-------
errormsg(str) or True
"""
- logger.debug("Entering lib API: verify_bgp_convergence()")
+ result = False
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ tgen = get_topogen()
for router, rnode in tgen.routers().items():
- if dut is not None and dut != router:
+ if 'bgp' not in topo['routers'][router]:
continue
- if "bgp" not in topo["routers"][router]:
+ if dut is not None and dut != router:
continue
logger.info("Verifying BGP Convergence on router %s:", router)
- show_bgp_json = run_frr_cmd(rnode, "show bgp vrf all summary json", isjson=True)
+ show_bgp_json = run_frr_cmd(rnode, "show bgp vrf all summary json",
+ isjson=True)
# Verifying output dictionary show_bgp_json is empty or not
if not bool(show_bgp_json):
errormsg = "BGP is not running"
@@ -1115,100 +1251,6 @@ def verify_bgp_convergence(tgen, topo, dut=None, expected=True):
# To find neighbor ip type
bgp_addr_type = bgp_data["address_family"]
- if "ipv4" in bgp_addr_type or "ipv6" in bgp_addr_type:
- for addr_type in bgp_addr_type.keys():
- if not check_address_types(addr_type):
- continue
- total_peer = 0
-
- bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"]
-
- for bgp_neighbor in bgp_neighbors:
- total_peer += len(bgp_neighbors[bgp_neighbor]["dest_link"])
-
- for addr_type in bgp_addr_type.keys():
- if not check_address_types(addr_type):
- continue
- bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"]
-
- no_of_peer = 0
- for bgp_neighbor, peer_data in bgp_neighbors.items():
- for dest_link in peer_data["dest_link"].keys():
- data = topo["routers"][bgp_neighbor]["links"]
- if dest_link in data:
- peer_details = peer_data["dest_link"][dest_link]
- # for link local neighbors
- if (
- "neighbor_type" in peer_details
- and peer_details["neighbor_type"] == "link-local"
- ):
- neighbor_ip = get_ipv6_linklocal_address(
- topo["routers"], bgp_neighbor, dest_link
- )
- elif "source_link" in peer_details:
- neighbor_ip = topo["routers"][bgp_neighbor][
- "links"
- ][peer_details["source_link"]][addr_type].split(
- "/"
- )[
- 0
- ]
- elif (
- "neighbor_type" in peer_details
- and peer_details["neighbor_type"] == "unnumbered"
- ):
- neighbor_ip = data[dest_link]["peer-interface"]
- else:
- neighbor_ip = data[dest_link][addr_type].split("/")[
- 0
- ]
- nh_state = None
-
- if addr_type == "ipv4":
- if "ipv4Unicast" in show_bgp_json[vrf]:
- ipv4_data = show_bgp_json[vrf]["ipv4Unicast"][
- "peers"
- ]
- nh_state = ipv4_data[neighbor_ip]["state"]
- else:
- if "ipv6Unicast" in show_bgp_json[vrf]:
- ipv6_data = show_bgp_json[vrf]["ipv6Unicast"][
- "peers"
- ]
- nh_state = ipv6_data[neighbor_ip]["state"]
- if nh_state == "Established":
- no_of_peer += 1
-
- if "l2vpn" in bgp_addr_type:
- if "neighbor" not in bgp_addr_type["l2vpn"]["evpn"]:
- if no_of_peer == total_peer:
- logger.info(
- "[DUT: %s] VRF: %s, BGP is Converged for %s address-family",
- router,
- vrf,
- addr_type,
- )
- else:
- errormsg = (
- "[DUT: %s] VRF: %s, BGP is not converged for %s address-family"
- % (router, vrf, addr_type)
- )
- return errormsg
- else:
- if no_of_peer == total_peer:
- logger.info(
- "[DUT: %s] VRF: %s, BGP is Converged for %s address-family",
- router,
- vrf,
- addr_type,
- )
- else:
- errormsg = (
- "[DUT: %s] VRF: %s, BGP is not converged for %s address-family"
- % (router, vrf, addr_type)
- )
- return errormsg
-
if "l2vpn" in bgp_addr_type:
total_evpn_peer = 0
@@ -1224,46 +1266,120 @@ def verify_bgp_convergence(tgen, topo, dut=None, expected=True):
data = topo["routers"][bgp_neighbor]["links"]
for dest_link in dest_link_dict.keys():
if dest_link in data:
- peer_details = peer_data[_addr_type][dest_link]
+ peer_details = \
+ peer_data[_addr_type][dest_link]
- neighbor_ip = data[dest_link][_addr_type].split("/")[0]
+ neighbor_ip = \
+ data[dest_link][_addr_type].split(
+ "/")[0]
nh_state = None
- if (
- "ipv4Unicast" in show_bgp_json[vrf]
- or "ipv6Unicast" in show_bgp_json[vrf]
- ):
- errormsg = (
- "[DUT: %s] VRF: %s, "
- "ipv4Unicast/ipv6Unicast"
- " address-family present"
- " under l2vpn" % (router, vrf)
- )
+ if "ipv4Unicast" in show_bgp_json[vrf] or \
+ "ipv6Unicast" in show_bgp_json[vrf]:
+ errormsg = ("[DUT: %s] VRF: %s, "
+ "ipv4Unicast/ipv6Unicast"
+ " address-family present"
+ " under l2vpn" % (router,
+ vrf))
return errormsg
- l2VpnEvpn_data = show_bgp_json[vrf]["l2VpnEvpn"][
- "peers"
- ]
- nh_state = l2VpnEvpn_data[neighbor_ip]["state"]
+ l2VpnEvpn_data = \
+ show_bgp_json[vrf]["l2VpnEvpn"][
+ "peers"]
+ nh_state = \
+ l2VpnEvpn_data[neighbor_ip]["state"]
if nh_state == "Established":
no_of_evpn_peer += 1
if no_of_evpn_peer == total_evpn_peer:
- logger.info(
- "[DUT: %s] VRF: %s, BGP is Converged for " "epvn peers",
- router,
- vrf,
- )
+ logger.info("[DUT: %s] VRF: %s, BGP is Converged for "
+ "epvn peers", router, vrf)
+ result = True
else:
- errormsg = (
- "[DUT: %s] VRF: %s, BGP is not converged "
- "for evpn peers" % (router, vrf)
- )
+ errormsg = ("[DUT: %s] VRF: %s, BGP is not converged "
+ "for evpn peers" % (router, vrf))
return errormsg
+ else:
+ total_peer = 0
+ for addr_type in bgp_addr_type.keys():
+ if not check_address_types(addr_type):
+ continue
- logger.debug("Exiting API: verify_bgp_convergence()")
- return True
+ bgp_neighbors = \
+ bgp_addr_type[addr_type]["unicast"]["neighbor"]
+
+ for bgp_neighbor in bgp_neighbors:
+ total_peer += \
+ len(bgp_neighbors[bgp_neighbor]["dest_link"])
+
+ no_of_peer = 0
+ for addr_type in bgp_addr_type.keys():
+ if not check_address_types(addr_type):
+ continue
+ bgp_neighbors = \
+ bgp_addr_type[addr_type]["unicast"]["neighbor"]
+
+ for bgp_neighbor, peer_data in bgp_neighbors.items():
+ for dest_link in peer_data["dest_link"].\
+ keys():
+ data = \
+ topo["routers"][bgp_neighbor]["links"]
+ if dest_link in data:
+ peer_details = \
+ peer_data['dest_link'][dest_link]
+ # for link local neighbors
+ if "neighbor_type" in peer_details and \
+ peer_details["neighbor_type"] == \
+ 'link-local':
+ intf = topo["routers"][bgp_neighbor][
+ "links"][dest_link]["interface"]
+ neighbor_ip = get_frr_ipv6_linklocal(
+ tgen, bgp_neighbor, intf)
+ elif "source_link" in peer_details:
+ neighbor_ip = \
+ topo["routers"][bgp_neighbor][
+ "links"][peer_details[
+ 'source_link']][
+ addr_type].\
+ split("/")[0]
+ elif "neighbor_type" in peer_details and \
+ peer_details["neighbor_type"] == \
+ 'unnumbered':
+ neighbor_ip = \
+ data[dest_link]["peer-interface"]
+ else:
+ neighbor_ip = \
+ data[dest_link][addr_type].split(
+ "/")[0]
+ nh_state = None
+ neighbor_ip = neighbor_ip.lower()
+ if addr_type == "ipv4":
+ ipv4_data = show_bgp_json[vrf][
+ "ipv4Unicast"]["peers"]
+ nh_state = \
+ ipv4_data[neighbor_ip]["state"]
+ else:
+ ipv6_data = show_bgp_json[vrf][
+ "ipv6Unicast"]["peers"]
+ if neighbor_ip in ipv6_data:
+ nh_state = \
+ ipv6_data[neighbor_ip]["state"]
+
+ if nh_state == "Established":
+ no_of_peer += 1
+
+ if no_of_peer == total_peer and no_of_peer > 0:
+ logger.info("[DUT: %s] VRF: %s, BGP is Converged",
+ router, vrf)
+ result = True
+ else:
+ errormsg = ("[DUT: %s] VRF: %s, BGP is not converged"
+ % (router, vrf))
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return result
@retry(retry_timeout=16)
diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py
index d659b8d52d..07bb5153ab 100644
--- a/tests/topotests/lib/common_config.py
+++ b/tests/topotests/lib/common_config.py
@@ -365,7 +365,7 @@ def create_common_configuration(
return True
-def kill_router_daemons(tgen, router, daemons):
+def kill_router_daemons(tgen, router, daemons, save_config=True):
"""
Router's current config would be saved to /etc/frr/ for each daemon
and daemon would be killed forcefully using SIGKILL.
@@ -379,9 +379,10 @@ def kill_router_daemons(tgen, router, daemons):
try:
router_list = tgen.routers()
- # Saving router config to /etc/frr, which will be loaded to router
- # when it starts
- router_list[router].vtysh_cmd("write memory")
+ if save_config:
+ # Saving router config to /etc/frr, which will be loaded to router
+ # when it starts
+ router_list[router].vtysh_cmd("write memory")
# Kill Daemons
result = router_list[router].killDaemons(daemons)
@@ -496,7 +497,7 @@ def reset_config_on_routers(tgen, routerName=None):
f.close()
run_cfg_file = "{}/{}/frr.sav".format(TMPDIR, rname)
init_cfg_file = "{}/{}/frr_json_initial.conf".format(TMPDIR, rname)
- command = "/usr/lib/frr/frr-reload.py --input {} --test {} > {}".format(
+ command = "/usr/lib/frr/frr-reload.py --test --test-reset --input {} {} > {}".format(
run_cfg_file, init_cfg_file, dname
)
result = call(command, shell=True, stderr=SUB_STDOUT, stdout=SUB_PIPE)
@@ -526,37 +527,9 @@ def reset_config_on_routers(tgen, routerName=None):
raise InvalidCLIError(out_data)
raise InvalidCLIError("Unknown error in %s", output)
- f = open(dname, "r")
delta = StringIO()
- delta.write("configure terminal\n")
- t_delta = f.read()
-
- # Don't disable debugs
- check_debug = True
-
- for line in t_delta.split("\n"):
- line = line.strip()
- if line == "Lines To Delete" or line == "===============" or not line:
- continue
-
- if line == "Lines To Add":
- check_debug = False
- continue
-
- if line == "============" or not line:
- continue
-
- # Leave debugs and log output alone
- if check_debug:
- if "debug" in line or "log file" in line:
- continue
-
- delta.write(line)
- delta.write("\n")
-
- f.close()
-
- delta.write("end\n")
+ with open(dname, "r") as f:
+ delta.write(f.read())
output = router.vtysh_multicmd(delta.getvalue(), pretty_output=False)
@@ -636,6 +609,7 @@ def load_config_to_router(tgen, routerName, save_bkup=False):
return True
+
def get_frr_ipv6_linklocal(tgen, router, intf=None, vrf=None):
"""
API to get the link local ipv6 address of a particular interface using
@@ -668,38 +642,48 @@ def get_frr_ipv6_linklocal(tgen, router, intf=None, vrf=None):
else:
cmd = "show interface"
- ifaces = router_list[router].run('vtysh -c "{}"'.format(cmd))
-
- # Fix newlines (make them all the same)
- ifaces = ("\n".join(ifaces.splitlines()) + "\n").splitlines()
-
- interface = None
- ll_per_if_count = 0
- for line in ifaces:
- # Interface name
- m = re_search("Interface ([a-zA-Z0-9-]+) is", line)
- if m:
- interface = m.group(1).split(" ")[0]
- ll_per_if_count = 0
-
- # Interface ip
- m1 = re_search("inet6 (fe80[:a-fA-F0-9]+[/0-9]+)", line)
- if m1:
- local = m1.group(1)
- ll_per_if_count += 1
- if ll_per_if_count > 1:
- linklocal += [["%s-%s" % (interface, ll_per_if_count), local]]
- else:
- linklocal += [[interface, local]]
-
- if linklocal:
- if intf:
- return [_linklocal[1] for _linklocal in linklocal if _linklocal[0] == intf][
- 0
- ].split("/")[0]
- return linklocal
- else:
- errormsg = "Link local ip missing on router {}"
+ linklocal = []
+ if vrf:
+ cmd = "show interface vrf {}".format(vrf)
+ else:
+ cmd = "show interface"
+ for chk_ll in range(0, 60):
+ sleep(1/4)
+ ifaces = router_list[router].run('vtysh -c "{}"'.format(cmd))
+ # Fix newlines (make them all the same)
+ ifaces = ('\n'.join(ifaces.splitlines()) + '\n').splitlines()
+
+ interface = None
+ ll_per_if_count = 0
+ for line in ifaces:
+ # Interface name
+ m = re_search('Interface ([a-zA-Z0-9-]+) is', line)
+ if m:
+ interface = m.group(1).split(" ")[0]
+ ll_per_if_count = 0
+
+ # Interface ip
+ m1 = re_search('inet6 (fe80[:a-fA-F0-9]+[\/0-9]+)',
+ line)
+ if m1:
+ local = m1.group(1)
+ ll_per_if_count += 1
+ if ll_per_if_count > 1:
+ linklocal += [["%s-%s" %
+ (interface, ll_per_if_count), local]]
+ else:
+ linklocal += [[interface, local]]
+
+ try:
+ if linklocal:
+ if intf:
+ return [_linklocal[1] for _linklocal in linklocal if _linklocal[0]==intf][0].\
+ split("/")[0]
+ return linklocal
+ except IndexError:
+ continue
+
+ errormsg = "Link local ip missing on router {}".format(router)
return errormsg
@@ -1845,6 +1829,14 @@ def create_interfaces_cfg(tgen, topo, build=False):
else:
interface_data.append("ipv6 address {}".format(intf_addr))
+ # Wait for vrf interfaces to get link local address once they are up
+ if not destRouterLink == 'lo' and 'vrf' in topo[c_router][
+ 'links'][destRouterLink]:
+ vrf = topo[c_router]['links'][destRouterLink]['vrf']
+ intf = topo[c_router]['links'][destRouterLink]['interface']
+ ll = get_frr_ipv6_linklocal(tgen, c_router, intf=intf,
+ vrf = vrf)
+
if "ipv6-link-local" in data:
intf_addr = c_data["links"][destRouterLink]["ipv6-link-local"]
@@ -1867,7 +1859,7 @@ def create_interfaces_cfg(tgen, topo, build=False):
)
if "ospf6" in data:
interface_data += _create_interfaces_ospf_cfg(
- "ospf6", c_data, data, ospf_keywords
+ "ospf6", c_data, data, ospf_keywords + ["area"]
)
result = create_common_configuration(
diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py
index dc9fe0fcca..6aa7a2c0a9 100644
--- a/tests/topotests/lib/ospf.py
+++ b/tests/topotests/lib/ospf.py
@@ -28,6 +28,7 @@ from time import sleep
from lib.topolog import logger
from lib.topotest import frr_unicode
from ipaddress import IPv6Address
+
# Import common_config to use commomnly used APIs
from lib.common_config import (
create_common_configuration,
@@ -89,8 +90,7 @@ def create_router_ospf(tgen, topo, input_dict=None, build=False, load_config=Tru
logger.debug("Router %s: 'ospf' not present in input_dict", router)
continue
- result = __create_ospf_global(
- tgen, input_dict, router, build, load_config)
+ result = __create_ospf_global(tgen, input_dict, router, build, load_config)
if result is True:
ospf_data = input_dict[router]["ospf"]
@@ -100,7 +100,8 @@ def create_router_ospf(tgen, topo, input_dict=None, build=False, load_config=Tru
continue
result = __create_ospf_global(
- tgen, input_dict, router, build, load_config, ospf='ospf6')
+ tgen, input_dict, router, build, load_config, ospf="ospf6"
+ )
if result is True:
ospf_data = input_dict[router]["ospf6"]
@@ -172,7 +173,6 @@ def __create_ospf_global(
config_data.append(cmd)
-
# router id
router_id = ospf_data.setdefault("router_id", None)
del_router_id = ospf_data.setdefault("del_router_id", False)
@@ -187,8 +187,7 @@ def __create_ospf_global(
if del_log_adj_changes:
config_data.append("no log-adjacency-changes detail")
if log_adj_changes:
- config_data.append("log-adjacency-changes {}".format(
- log_adj_changes))
+ config_data.append("log-adjacency-changes {}".format(log_adj_changes))
# aggregation timer
aggr_timer = ospf_data.setdefault("aggr_timer", None)
@@ -196,8 +195,7 @@ def __create_ospf_global(
if del_aggr_timer:
config_data.append("no aggregation timer")
if aggr_timer:
- config_data.append("aggregation timer {}".format(
- aggr_timer))
+ config_data.append("aggregation timer {}".format(aggr_timer))
# maximum path information
ecmp_data = ospf_data.setdefault("maximum-paths", {})
@@ -245,12 +243,13 @@ def __create_ospf_global(
cmd = "no {}".format(cmd)
config_data.append(cmd)
- #def route information
+ # def route information
def_rte_data = ospf_data.setdefault("default-information", {})
if def_rte_data:
if "originate" not in def_rte_data:
- logger.debug("Router %s: 'originate key' not present in "
- "input_dict", router)
+ logger.debug(
+ "Router %s: 'originate key' not present in " "input_dict", router
+ )
else:
cmd = "default-information originate"
@@ -261,12 +260,10 @@ def __create_ospf_global(
cmd = cmd + " metric {}".format(def_rte_data["metric"])
if "metric-type" in def_rte_data:
- cmd = cmd + " metric-type {}".format(def_rte_data[
- "metric-type"])
+ cmd = cmd + " metric-type {}".format(def_rte_data["metric-type"])
if "route-map" in def_rte_data:
- cmd = cmd + " route-map {}".format(def_rte_data[
- "route-map"])
+ cmd = cmd + " route-map {}".format(def_rte_data["route-map"])
del_action = def_rte_data.setdefault("delete", False)
if del_action:
@@ -288,19 +285,19 @@ def __create_ospf_global(
config_data.append(cmd)
try:
- if "area" in input_dict[router]['links'][neighbor][
- 'ospf6']:
+ if "area" in input_dict[router]["links"][neighbor]["ospf6"]:
iface = input_dict[router]["links"][neighbor]["interface"]
cmd = "interface {} area {}".format(
- iface, input_dict[router]['links'][neighbor][
- 'ospf6']['area'])
- if input_dict[router]['links'][neighbor].setdefault(
- "delete", False):
+ iface,
+ input_dict[router]["links"][neighbor]["ospf6"]["area"],
+ )
+ if input_dict[router]["links"][neighbor].setdefault(
+ "delete", False
+ ):
cmd = "no {}".format(cmd)
config_data.append(cmd)
except KeyError:
- pass
-
+ pass
# summary information
summary_data = ospf_data.setdefault("summary-address", {})
@@ -420,6 +417,7 @@ def config_ospf_interface(tgen, topo, input_dict=None, build=False, load_config=
True or False
"""
logger.debug("Enter lib config_ospf_interface")
+ result = False
if not input_dict:
input_dict = deepcopy(topo)
else:
@@ -502,7 +500,7 @@ def config_ospf_interface(tgen, topo, input_dict=None, build=False, load_config=
# interface ospf mtu
if data_ospf_mtu:
cmd = "ip ospf mtu-ignore"
- if 'del_action' in ospf_data:
+ if "del_action" in ospf_data:
cmd = "no {}".format(cmd)
config_data.append(cmd)
@@ -543,8 +541,7 @@ def clear_ospf(tgen, router, ospf=None):
version = "ip"
cmd = "clear {} ospf interface".format(version)
- logger.info(
- "Clearing ospf process on router %s.. using command '%s'", router, cmd)
+ logger.info("Clearing ospf process on router %s.. using command '%s'", router, cmd)
run_frr_cmd(rnode, cmd)
logger.debug("Exiting lib API: clear_ospf()")
@@ -774,7 +771,7 @@ def verify_ospf_neighbor(tgen, topo, dut=None, input_dict=None, lan=False, expec
################################
# Verification procs
################################
-@retry(retry_timeout=20)
+@retry(retry_timeout=50)
def verify_ospf6_neighbor(tgen, topo, dut=None, input_dict=None, lan=False):
"""
This API is to verify ospf neighborship by running
@@ -825,105 +822,133 @@ def verify_ospf6_neighbor(tgen, topo, dut=None, input_dict=None, lan=False):
if input_dict:
for router, rnode in tgen.routers().items():
- if 'ospf6' not in topo['routers'][router]:
+ if "ospf6" not in topo["routers"][router]:
continue
if dut is not None and dut != router:
continue
logger.info("Verifying OSPF neighborship on router %s:", router)
- show_ospf_json = run_frr_cmd(rnode,
- "show ipv6 ospf neighbor json", isjson=True)
+ show_ospf_json = run_frr_cmd(
+ rnode, "show ipv6 ospf neighbor json", isjson=True
+ )
# Verifying output dictionary show_ospf_json is empty or not
if not bool(show_ospf_json):
errormsg = "OSPF6 is not running"
return errormsg
ospf_data_list = input_dict[router]["ospf6"]
- ospf_nbr_list = ospf_data_list['neighbors']
+ ospf_nbr_list = ospf_data_list["neighbors"]
for ospf_nbr, nbr_data in ospf_nbr_list.items():
- data_ip = data_rid = topo['routers'][ospf_nbr]['ospf6']['router_id']
+
+ try:
+ data_ip = data_rid = topo["routers"][ospf_nbr]["ospf6"]["router_id"]
+ except KeyError:
+ data_ip = data_rid = topo["routers"][nbr_data["nbr"]]["ospf6"][
+ "router_id"
+ ]
+
if ospf_nbr in data_ip:
nbr_details = nbr_data[ospf_nbr]
elif lan:
- for switch in topo['switches']:
- if 'ospf6' in topo['switches'][switch]['links'][router]:
+ for switch in topo["switches"]:
+ if "ospf6" in topo["switches"][switch]["links"][router]:
neighbor_ip = data_ip
else:
continue
else:
- neighbor_ip = data_ip[router]['ipv6'].split("/")[0]
+ neighbor_ip = data_ip[router]["ipv6"].split("/")[0]
nh_state = None
neighbor_ip = neighbor_ip.lower()
nbr_rid = data_rid
- get_index_val = dict((d['neighborId'], dict( \
- d, index=index)) for (index, d) in enumerate( \
- show_ospf_json['neighbors']))
+ get_index_val = dict(
+ (d["neighborId"], dict(d, index=index))
+ for (index, d) in enumerate(show_ospf_json["neighbors"])
+ )
try:
- nh_state = get_index_val.get(neighbor_ip)['state']
- intf_state = get_index_val.get(neighbor_ip)['ifState']
+ nh_state = get_index_val.get(neighbor_ip)["state"]
+ intf_state = get_index_val.get(neighbor_ip)["ifState"]
except TypeError:
- errormsg = "[DUT: {}] OSPF peer {} missing,from "\
- "{} ".format(router,
- nbr_rid, ospf_nbr)
+ errormsg = "[DUT: {}] OSPF peer {} missing,from " "{} ".format(
+ router, nbr_rid, ospf_nbr
+ )
return errormsg
- nbr_state = nbr_data.setdefault("state",None)
- nbr_role = nbr_data.setdefault("role",None)
+ nbr_state = nbr_data.setdefault("state", None)
+ nbr_role = nbr_data.setdefault("role", None)
if nbr_state:
if nbr_state == nh_state:
- logger.info("[DUT: {}] OSPF6 Nbr is {}:{} State {}".format
- (router, ospf_nbr, nbr_rid, nh_state))
+ logger.info(
+ "[DUT: {}] OSPF6 Nbr is {}:{} State {}".format(
+ router, ospf_nbr, nbr_rid, nh_state
+ )
+ )
result = True
else:
- errormsg = ("[DUT: {}] OSPF6 is not Converged, neighbor"
- " state is {} , Expected state is {}".format(router,
- nh_state, nbr_state))
+ errormsg = (
+ "[DUT: {}] OSPF6 is not Converged, neighbor"
+ " state is {} , Expected state is {}".format(
+ router, nh_state, nbr_state
+ )
+ )
return errormsg
if nbr_role:
if nbr_role == intf_state:
- logger.info("[DUT: {}] OSPF6 Nbr is {}: {} Role {}".format(
- router, ospf_nbr, nbr_rid, nbr_role))
+ logger.info(
+ "[DUT: {}] OSPF6 Nbr is {}: {} Role {}".format(
+ router, ospf_nbr, nbr_rid, nbr_role
+ )
+ )
else:
- errormsg = ("[DUT: {}] OSPF6 is not Converged with rid"
- "{}, role is {}, Expected role is {}".format(router,
- nbr_rid, intf_state, nbr_role))
+ errormsg = (
+ "[DUT: {}] OSPF6 is not Converged with rid"
+ "{}, role is {}, Expected role is {}".format(
+ router, nbr_rid, intf_state, nbr_role
+ )
+ )
return errormsg
continue
else:
for router, rnode in tgen.routers().items():
- if 'ospf6' not in topo['routers'][router]:
+ if "ospf6" not in topo["routers"][router]:
continue
if dut is not None and dut != router:
continue
logger.info("Verifying OSPF6 neighborship on router %s:", router)
- show_ospf_json = run_frr_cmd(rnode,
- "show ipv6 ospf neighbor json", isjson=True)
+ show_ospf_json = run_frr_cmd(
+ rnode, "show ipv6 ospf neighbor json", isjson=True
+ )
# Verifying output dictionary show_ospf_json is empty or not
if not bool(show_ospf_json):
errormsg = "OSPF6 is not running"
return errormsg
ospf_data_list = topo["routers"][router]["ospf6"]
- ospf_neighbors = ospf_data_list['neighbors']
+ ospf_neighbors = ospf_data_list["neighbors"]
total_peer = 0
total_peer = len(ospf_neighbors.keys())
no_of_ospf_nbr = 0
- ospf_nbr_list = ospf_data_list['neighbors']
+ ospf_nbr_list = ospf_data_list["neighbors"]
no_of_peer = 0
for ospf_nbr, nbr_data in ospf_nbr_list.items():
- data_ip = data_rid = topo['routers'][ospf_nbr]['ospf6']['router_id']
+ try:
+ data_ip = data_rid = topo["routers"][ospf_nbr]["ospf6"]["router_id"]
+ except KeyError:
+ data_ip = data_rid = topo["routers"][nbr_data["nbr"]]["ospf6"][
+ "router_id"
+ ]
+
if ospf_nbr in data_ip:
nbr_details = nbr_data[ospf_nbr]
elif lan:
- for switch in topo['switches']:
- if 'ospf6' in topo['switches'][switch]['links'][router]:
+ for switch in topo["switches"]:
+ if "ospf6" in topo["switches"][switch]["links"][router]:
neighbor_ip = data_ip
else:
continue
@@ -933,26 +958,27 @@ def verify_ospf6_neighbor(tgen, topo, dut=None, input_dict=None, lan=False):
nh_state = None
neighbor_ip = neighbor_ip.lower()
nbr_rid = data_rid
- get_index_val = dict((d['neighborId'], dict( \
- d, index=index)) for (index, d) in enumerate( \
- show_ospf_json['neighbors']))
+ get_index_val = dict(
+ (d["neighborId"], dict(d, index=index))
+ for (index, d) in enumerate(show_ospf_json["neighbors"])
+ )
try:
- nh_state = get_index_val.get(neighbor_ip)['state']
- intf_state = get_index_val.get(neighbor_ip)['ifState']
+ nh_state = get_index_val.get(neighbor_ip)["state"]
+ intf_state = get_index_val.get(neighbor_ip)["ifState"]
except TypeError:
- errormsg = "[DUT: {}] OSPF peer {} missing,from "\
- "{} ".format(router,
- nbr_rid, ospf_nbr)
+ errormsg = "[DUT: {}] OSPF peer {} missing,from " "{} ".format(
+ router, nbr_rid, ospf_nbr
+ )
return errormsg
- if nh_state == 'Full':
+ if nh_state == "Full":
no_of_peer += 1
if no_of_peer == total_peer:
logger.info("[DUT: {}] OSPF6 is Converged".format(router))
result = True
else:
- errormsg = ("[DUT: {}] OSPF6 is not Converged".format(router))
+ errormsg = "[DUT: {}] OSPF6 is not Converged".format(router)
return errormsg
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
@@ -1627,31 +1653,34 @@ def verify_ospf6_rib(tgen, dut, input_dict, next_hop=None,
found_routes = []
missing_routes = []
- if "static_routes" in input_dict[routerInput] or \
- "prefix" in input_dict[routerInput]:
+ if (
+ "static_routes" in input_dict[routerInput]
+ or "prefix" in input_dict[routerInput]
+ ):
if "prefix" in input_dict[routerInput]:
static_routes = input_dict[routerInput]["prefix"]
else:
static_routes = input_dict[routerInput]["static_routes"]
-
for static_route in static_routes:
cmd = "{}".format(command)
cmd = "{} json".format(cmd)
- ospf_rib_json = run_frr_cmd(rnode, cmd, isjson=True)
+ ospf_rib_json = run_frr_cmd(rnode, cmd, isjson=True)
# Fix for PR 2644182
try:
- ospf_rib_json = ospf_rib_json['routes']
+ ospf_rib_json = ospf_rib_json["routes"]
except KeyError:
pass
# Verifying output dictionary ospf_rib_json is not empty
if bool(ospf_rib_json) is False:
- errormsg = "[DUT: {}] No routes found in OSPF6 route " \
+ errormsg = (
+ "[DUT: {}] No routes found in OSPF6 route "
"table".format(router)
+ )
return errormsg
network = static_route["network"]
@@ -1659,7 +1688,6 @@ def verify_ospf6_rib(tgen, dut, input_dict, next_hop=None,
_tag = static_route.setdefault("tag", None)
_rtype = static_route.setdefault("routeType", None)
-
# Generating IPs for verification
ip_list = generate_ips(network, no_of_ip)
st_found = False
@@ -1668,7 +1696,7 @@ def verify_ospf6_rib(tgen, dut, input_dict, next_hop=None,
st_rt = str(ipaddress.ip_network(frr_unicode(st_rt)))
_addr_type = validate_ip_address(st_rt)
- if _addr_type != 'ipv6':
+ if _addr_type != "ipv6":
continue
if st_rt in ospf_rib_json:
@@ -1681,17 +1709,26 @@ def verify_ospf6_rib(tgen, dut, input_dict, next_hop=None,
next_hop = [next_hop]
for mnh in range(0, len(ospf_rib_json[st_rt])):
- if 'fib' in ospf_rib_json[st_rt][
- mnh]["nextHops"][0]:
- found_hops.append([rib_r[
- "ip"] for rib_r in ospf_rib_json[
- st_rt][mnh]["nextHops"]])
+ if (
+ "fib"
+ in ospf_rib_json[st_rt][mnh]["nextHops"][0]
+ ):
+ found_hops.append(
+ [
+ rib_r["ip"]
+ for rib_r in ospf_rib_json[st_rt][mnh][
+ "nextHops"
+ ]
+ ]
+ )
if found_hops[0]:
- missing_list_of_nexthops = \
- set(found_hops[0]).difference(next_hop)
- additional_nexthops_in_required_nhs = \
- set(next_hop).difference(found_hops[0])
+ missing_list_of_nexthops = set(
+ found_hops[0]
+ ).difference(next_hop)
+ additional_nexthops_in_required_nhs = set(
+ next_hop
+ ).difference(found_hops[0])
if additional_nexthops_in_required_nhs:
logger.info(
@@ -1699,13 +1736,18 @@ def verify_ospf6_rib(tgen, dut, input_dict, next_hop=None,
"%s is not active for route %s in "
"RIB of router %s\n",
additional_nexthops_in_required_nhs,
- st_rt, dut)
+ st_rt,
+ dut,
+ )
errormsg = (
"Nexthop {} is not active"
" for route {} in RIB of router"
" {}\n".format(
- additional_nexthops_in_required_nhs,
- st_rt, dut))
+ additional_nexthops_in_required_nhs,
+ st_rt,
+ dut,
+ )
+ )
return errormsg
else:
nh_found = True
@@ -1713,98 +1755,118 @@ def verify_ospf6_rib(tgen, dut, input_dict, next_hop=None,
elif next_hop and fib is None:
if type(next_hop) is not list:
next_hop = [next_hop]
- found_hops = [rib_r['nextHop'] for rib_r in
- ospf_rib_json[st_rt][
- "nextHops"]]
+ found_hops = [
+ rib_r["nextHop"]
+ for rib_r in ospf_rib_json[st_rt]["nextHops"]
+ ]
if found_hops:
- missing_list_of_nexthops = \
- set(found_hops).difference(next_hop)
- additional_nexthops_in_required_nhs = \
- set(next_hop).difference(found_hops)
+ missing_list_of_nexthops = set(
+ found_hops
+ ).difference(next_hop)
+ additional_nexthops_in_required_nhs = set(
+ next_hop
+ ).difference(found_hops)
if additional_nexthops_in_required_nhs:
logger.info(
- "Missing nexthop %s for route"\
- " %s in RIB of router %s\n", \
- additional_nexthops_in_required_nhs, \
- st_rt, dut)
- errormsg=("Nexthop {} is Missing for "\
- "route {} in RIB of router {}\n".format(
+ "Missing nexthop %s for route"
+ " %s in RIB of router %s\n",
additional_nexthops_in_required_nhs,
- st_rt, dut))
+ st_rt,
+ dut,
+ )
+ errormsg = (
+ "Nexthop {} is Missing for "
+ "route {} in RIB of router {}\n".format(
+ additional_nexthops_in_required_nhs,
+ st_rt,
+ dut,
+ )
+ )
return errormsg
else:
nh_found = True
if _rtype:
- if "destinationType" not in ospf_rib_json[
- st_rt]:
- errormsg = ("[DUT: {}]: destinationType missing"
- "for route {} in OSPF RIB \n".\
- format(dut, st_rt))
+ if "destinationType" not in ospf_rib_json[st_rt]:
+ errormsg = (
+ "[DUT: {}]: destinationType missing"
+ "for route {} in OSPF RIB \n".format(dut, st_rt)
+ )
return errormsg
- elif _rtype != ospf_rib_json[st_rt][
- "destinationType"]:
- errormsg = ("[DUT: {}]: destinationType mismatch"
- "for route {} in OSPF RIB \n".\
- format(dut, st_rt))
+ elif _rtype != ospf_rib_json[st_rt]["destinationType"]:
+ errormsg = (
+ "[DUT: {}]: destinationType mismatch"
+ "for route {} in OSPF RIB \n".format(dut, st_rt)
+ )
return errormsg
else:
- logger.info("DUT: {}]: Found destinationType {}"
- "for route {}".\
- format(dut, _rtype, st_rt))
+ logger.info(
+ "DUT: {}]: Found destinationType {}"
+ "for route {}".format(dut, _rtype, st_rt)
+ )
if tag:
- if "tag" not in ospf_rib_json[
- st_rt]:
- errormsg = ("[DUT: {}]: tag is not"
- " present for"
- " route {} in RIB \n".\
- format(dut, st_rt
- ))
+ if "tag" not in ospf_rib_json[st_rt]:
+ errormsg = (
+ "[DUT: {}]: tag is not"
+ " present for"
+ " route {} in RIB \n".format(dut, st_rt)
+ )
return errormsg
- if _tag != ospf_rib_json[
- st_rt]["tag"]:
- errormsg = ("[DUT: {}]: tag value {}"
- " is not matched for"
- " route {} in RIB \n".\
- format(dut, _tag, st_rt,
- ))
+ if _tag != ospf_rib_json[st_rt]["tag"]:
+ errormsg = (
+ "[DUT: {}]: tag value {}"
+ " is not matched for"
+ " route {} in RIB \n".format(
+ dut,
+ _tag,
+ st_rt,
+ )
+ )
return errormsg
if metric is not None:
- if "type2cost" not in ospf_rib_json[
- st_rt]:
- errormsg = ("[DUT: {}]: metric is"
- " not present for"
- " route {} in RIB \n".\
- format(dut, st_rt))
+ if "type2cost" not in ospf_rib_json[st_rt]:
+ errormsg = (
+ "[DUT: {}]: metric is"
+ " not present for"
+ " route {} in RIB \n".format(dut, st_rt)
+ )
return errormsg
- if metric != ospf_rib_json[
- st_rt]["type2cost"]:
- errormsg = ("[DUT: {}]: metric value "
- "{} is not matched for "
- "route {} in RIB \n".\
- format(dut, metric, st_rt,
- ))
+ if metric != ospf_rib_json[st_rt]["type2cost"]:
+ errormsg = (
+ "[DUT: {}]: metric value "
+ "{} is not matched for "
+ "route {} in RIB \n".format(
+ dut,
+ metric,
+ st_rt,
+ )
+ )
return errormsg
else:
missing_routes.append(st_rt)
if nh_found:
- logger.info("[DUT: {}]: Found next_hop {} for all OSPF"
- " routes in RIB".format(router, next_hop))
+ logger.info(
+ "[DUT: {}]: Found next_hop {} for all OSPF"
+ " routes in RIB".format(router, next_hop)
+ )
if len(missing_routes) > 0:
- errormsg = ("[DUT: {}]: Missing route in RIB, "
- "routes: {}".\
- format(dut, missing_routes))
+ errormsg = "[DUT: {}]: Missing route in RIB, " "routes: {}".format(
+ dut, missing_routes
+ )
return errormsg
if found_routes:
- logger.info("[DUT: %s]: Verified routes in RIB, found"
- " routes are: %s\n", dut, found_routes)
+ logger.info(
+ "[DUT: %s]: Verified routes in RIB, found" " routes are: %s\n",
+ dut,
+ found_routes,
+ )
result = True
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
@@ -1855,15 +1917,16 @@ def verify_ospf6_interface(tgen, topo, dut=None,lan=False, input_dict=None):
result = False
for router, rnode in tgen.routers().iteritems():
- if 'ospf6' not in topo['routers'][router]:
+ if "ospf6" not in topo["routers"][router]:
continue
if dut is not None and dut != router:
continue
logger.info("Verifying OSPF interface on router %s:", router)
- show_ospf_json = run_frr_cmd(rnode, "show ipv6 ospf interface json",
- isjson=True)
+ show_ospf_json = run_frr_cmd(
+ rnode, "show ipv6 ospf interface json", isjson=True
+ )
# Verifying output dictionary show_ospf_json is empty or not
if not bool(show_ospf_json):
@@ -1873,32 +1936,49 @@ def verify_ospf6_interface(tgen, topo, dut=None,lan=False, input_dict=None):
# To find neighbor ip type
ospf_intf_data = input_dict[router]["links"]
for ospf_intf, intf_data in ospf_intf_data.items():
- intf = topo['routers'][router]['links'][ospf_intf]['interface']
- if intf in show_ospf_json:
- for intf_attribute in intf_data['ospf6']:
- if intf_data['ospf6'][intf_attribute] is not list:
- if intf_data['ospf6'][intf_attribute] == show_ospf_json[
- intf][intf_attribute]:
- logger.info("[DUT: %s] OSPF6 interface %s: %s is %s",
- router, intf, intf_attribute, intf_data['ospf6'][
- intf_attribute])
- elif intf_data['ospf6'][intf_attribute] is list:
+ intf = topo["routers"][router]["links"][ospf_intf]["interface"]
+ if intf in show_ospf_json:
+ for intf_attribute in intf_data["ospf6"]:
+ if intf_data["ospf6"][intf_attribute] is not list:
+ if (
+ intf_data["ospf6"][intf_attribute]
+ == show_ospf_json[intf][intf_attribute]
+ ):
+ logger.info(
+ "[DUT: %s] OSPF6 interface %s: %s is %s",
+ router,
+ intf,
+ intf_attribute,
+ intf_data["ospf6"][intf_attribute],
+ )
+ elif intf_data["ospf6"][intf_attribute] is list:
for addr_list in len(show_ospf_json[intf][intf_attribute]):
- if show_ospf_json[intf][intf_attribute][addr_list][
- 'address'].split('/')[0] == intf_data['ospf6'][
- 'internetAddress'][0]['address']:
- break
+ if (
+ show_ospf_json[intf][intf_attribute][addr_list][
+ "address"
+ ].split("/")[0]
+ == intf_data["ospf6"]["internetAddress"][0]["address"]
+ ):
+ break
else:
- errormsg= "[DUT: {}] OSPF6 interface {}: {} is {}, \
- Expected is {}".format(router, intf, intf_attribute,
- intf_data['ospf6'][intf_attribute], intf_data['ospf6'][
- intf_attribute])
+ errormsg = "[DUT: {}] OSPF6 interface {}: {} is {}, \
+ Expected is {}".format(
+ router,
+ intf,
+ intf_attribute,
+ intf_data["ospf6"][intf_attribute],
+ intf_data["ospf6"][intf_attribute],
+ )
return errormsg
else:
- errormsg= "[DUT: {}] OSPF6 interface {}: {} is {}, \
- Expected is {}".format(router, intf, intf_attribute,
- intf_data['ospf6'][intf_attribute], intf_data['ospf6'][
- intf_attribute])
+ errormsg = "[DUT: {}] OSPF6 interface {}: {} is {}, \
+ Expected is {}".format(
+ router,
+ intf,
+ intf_attribute,
+ intf_data["ospf6"][intf_attribute],
+ intf_data["ospf6"][intf_attribute],
+ )
return errormsg
result = True
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
@@ -1956,16 +2036,14 @@ def verify_ospf6_database(tgen, topo, dut, input_dict):
router = dut
logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
- if 'ospf' not in topo['routers'][dut]:
- errormsg = "[DUT: {}] OSPF is not configured on the router.".format(
- dut)
+ if "ospf" not in topo["routers"][dut]:
+ errormsg = "[DUT: {}] OSPF is not configured on the router.".format(dut)
return errormsg
rnode = tgen.routers()[dut]
logger.info("Verifying OSPF interface on router %s:", dut)
- show_ospf_json = run_frr_cmd(rnode, "show ip ospf database json",
- isjson=True)
+ show_ospf_json = run_frr_cmd(rnode, "show ip ospf database json", isjson=True)
# Verifying output dictionary show_ospf_json is empty or not
if not bool(show_ospf_json):
errormsg = "OSPF is not running"
@@ -1973,167 +2051,209 @@ def verify_ospf6_database(tgen, topo, dut, input_dict):
# for inter and inter lsa's
ospf_db_data = input_dict.setdefault("areas", None)
- ospf_external_lsa = input_dict.setdefault(
- 'asExternalLinkStates', None)
+ ospf_external_lsa = input_dict.setdefault("asExternalLinkStates", None)
if ospf_db_data:
- for ospf_area, area_lsa in ospf_db_data.items():
- if ospf_area in show_ospf_json['areas']:
- if 'routerLinkStates' in area_lsa:
- for lsa in area_lsa['routerLinkStates']:
- for rtrlsa in show_ospf_json['areas'][ospf_area][
- 'routerLinkStates']:
- if lsa['lsaId'] == rtrlsa['lsaId'] and \
- lsa['advertisedRouter'] == rtrlsa[
- 'advertisedRouter']:
- result = True
- break
- if result:
- logger.info(
- "[DUT: %s] OSPF LSDB area %s:Router "
- "LSA %s", router, ospf_area, lsa)
+ for ospf_area, area_lsa in ospf_db_data.items():
+ if ospf_area in show_ospf_json["areas"]:
+ if "routerLinkStates" in area_lsa:
+ for lsa in area_lsa["routerLinkStates"]:
+ for rtrlsa in show_ospf_json["areas"][ospf_area][
+ "routerLinkStates"
+ ]:
+ if (
+ lsa["lsaId"] == rtrlsa["lsaId"]
+ and lsa["advertisedRouter"]
+ == rtrlsa["advertisedRouter"]
+ ):
+ result = True
break
- else:
- errormsg = \
- "[DUT: {}] OSPF LSDB area {}: expected" \
+ if result:
+ logger.info(
+ "[DUT: %s] OSPF LSDB area %s:Router " "LSA %s",
+ router,
+ ospf_area,
+ lsa,
+ )
+ break
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF LSDB area {}: expected"
" Router LSA is {}".format(router, ospf_area, lsa)
- return errormsg
+ )
+ return errormsg
- if 'networkLinkStates' in area_lsa:
- for lsa in area_lsa['networkLinkStates']:
- for netlsa in show_ospf_json['areas'][ospf_area][
- 'networkLinkStates']:
- if lsa in show_ospf_json['areas'][ospf_area][
- 'networkLinkStates']:
- if lsa['lsaId'] == netlsa['lsaId'] and \
- lsa['advertisedRouter'] == netlsa[
- 'advertisedRouter']:
- result = True
- break
- if result:
- logger.info(
- "[DUT: %s] OSPF LSDB area %s:Network "
- "LSA %s", router, ospf_area, lsa)
- break
- else:
- errormsg = \
- "[DUT: {}] OSPF LSDB area {}: expected" \
+ if "networkLinkStates" in area_lsa:
+ for lsa in area_lsa["networkLinkStates"]:
+ for netlsa in show_ospf_json["areas"][ospf_area][
+ "networkLinkStates"
+ ]:
+ if (
+ lsa
+ in show_ospf_json["areas"][ospf_area][
+ "networkLinkStates"
+ ]
+ ):
+ if (
+ lsa["lsaId"] == netlsa["lsaId"]
+ and lsa["advertisedRouter"]
+ == netlsa["advertisedRouter"]
+ ):
+ result = True
+ break
+ if result:
+ logger.info(
+ "[DUT: %s] OSPF LSDB area %s:Network " "LSA %s",
+ router,
+ ospf_area,
+ lsa,
+ )
+ break
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF LSDB area {}: expected"
" Network LSA is {}".format(router, ospf_area, lsa)
- return errormsg
+ )
+ return errormsg
- if 'summaryLinkStates' in area_lsa:
- for lsa in area_lsa['summaryLinkStates']:
- for t3lsa in show_ospf_json['areas'][ospf_area][
- 'summaryLinkStates']:
- if lsa['lsaId'] == t3lsa['lsaId'] and \
- lsa['advertisedRouter'] == t3lsa[
- 'advertisedRouter']:
- result = True
- break
- if result:
- logger.info(
- "[DUT: %s] OSPF LSDB area %s:Summary "
- "LSA %s", router, ospf_area, lsa)
+ if "summaryLinkStates" in area_lsa:
+ for lsa in area_lsa["summaryLinkStates"]:
+ for t3lsa in show_ospf_json["areas"][ospf_area][
+ "summaryLinkStates"
+ ]:
+ if (
+ lsa["lsaId"] == t3lsa["lsaId"]
+ and lsa["advertisedRouter"] == t3lsa["advertisedRouter"]
+ ):
+ result = True
break
- else:
- errormsg = \
- "[DUT: {}] OSPF LSDB area {}: expected" \
+ if result:
+ logger.info(
+ "[DUT: %s] OSPF LSDB area %s:Summary " "LSA %s",
+ router,
+ ospf_area,
+ lsa,
+ )
+ break
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF LSDB area {}: expected"
" Summary LSA is {}".format(router, ospf_area, lsa)
- return errormsg
+ )
+ return errormsg
- if 'nssaExternalLinkStates' in area_lsa:
- for lsa in area_lsa['nssaExternalLinkStates']:
- for t7lsa in show_ospf_json['areas'][ospf_area][
- 'nssaExternalLinkStates']:
- if lsa['lsaId'] == t7lsa['lsaId'] and \
- lsa['advertisedRouter'] == t7lsa[
- 'advertisedRouter']:
- result = True
- break
- if result:
- logger.info(
- "[DUT: %s] OSPF LSDB area %s:Type7 "
- "LSA %s", router, ospf_area, lsa)
+ if "nssaExternalLinkStates" in area_lsa:
+ for lsa in area_lsa["nssaExternalLinkStates"]:
+ for t7lsa in show_ospf_json["areas"][ospf_area][
+ "nssaExternalLinkStates"
+ ]:
+ if (
+ lsa["lsaId"] == t7lsa["lsaId"]
+ and lsa["advertisedRouter"] == t7lsa["advertisedRouter"]
+ ):
+ result = True
break
- else:
- errormsg = \
- "[DUT: {}] OSPF LSDB area {}: expected" \
+ if result:
+ logger.info(
+ "[DUT: %s] OSPF LSDB area %s:Type7 " "LSA %s",
+ router,
+ ospf_area,
+ lsa,
+ )
+ break
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF LSDB area {}: expected"
" Type7 LSA is {}".format(router, ospf_area, lsa)
- return errormsg
+ )
+ return errormsg
- if 'asbrSummaryLinkStates' in area_lsa:
- for lsa in area_lsa['asbrSummaryLinkStates']:
- for t4lsa in show_ospf_json['areas'][ospf_area][
- 'asbrSummaryLinkStates']:
- if lsa['lsaId'] == t4lsa['lsaId'] and \
- lsa['advertisedRouter'] == t4lsa[
- 'advertisedRouter']:
- result = True
- break
- if result:
- logger.info(
- "[DUT: %s] OSPF LSDB area %s:ASBR Summary "
- "LSA %s", router, ospf_area, lsa)
+ if "asbrSummaryLinkStates" in area_lsa:
+ for lsa in area_lsa["asbrSummaryLinkStates"]:
+ for t4lsa in show_ospf_json["areas"][ospf_area][
+ "asbrSummaryLinkStates"
+ ]:
+ if (
+ lsa["lsaId"] == t4lsa["lsaId"]
+ and lsa["advertisedRouter"] == t4lsa["advertisedRouter"]
+ ):
result = True
- else:
- errormsg = \
- "[DUT: {}] OSPF LSDB area {}: expected" \
- " ASBR Summary LSA is {}".format(
- router, ospf_area, lsa)
- return errormsg
+ break
+ if result:
+ logger.info(
+ "[DUT: %s] OSPF LSDB area %s:ASBR Summary " "LSA %s",
+ router,
+ ospf_area,
+ lsa,
+ )
+ result = True
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF LSDB area {}: expected"
+ " ASBR Summary LSA is {}".format(router, ospf_area, lsa)
+ )
+ return errormsg
- if 'linkLocalOpaqueLsa' in area_lsa:
- for lsa in area_lsa['linkLocalOpaqueLsa']:
- try:
- for lnklsa in show_ospf_json['areas'][ospf_area][
- 'linkLocalOpaqueLsa']:
- if lsa['lsaId'] in lnklsa['lsaId'] and \
- 'linkLocalOpaqueLsa' in show_ospf_json[
- 'areas'][ospf_area]:
- logger.info((
- "[DUT: FRR] OSPF LSDB area %s:Opaque-LSA"
- "%s", ospf_area, lsa))
- result = True
- else:
- errormsg = ("[DUT: FRR] OSPF LSDB area: {} "
- "expected Opaque-LSA is {}, Found is {}".format(
- ospf_area, lsa, show_ospf_json))
- raise ValueError (errormsg)
- return errormsg
- except KeyError:
- errormsg = ("[DUT: FRR] linkLocalOpaqueLsa Not "
- "present")
- return errormsg
+ if "linkLocalOpaqueLsa" in area_lsa:
+ for lsa in area_lsa["linkLocalOpaqueLsa"]:
+ try:
+ for lnklsa in show_ospf_json["areas"][ospf_area][
+ "linkLocalOpaqueLsa"
+ ]:
+ if (
+ lsa["lsaId"] in lnklsa["lsaId"]
+ and "linkLocalOpaqueLsa"
+ in show_ospf_json["areas"][ospf_area]
+ ):
+ logger.info(
+ (
+ "[DUT: FRR] OSPF LSDB area %s:Opaque-LSA"
+ "%s",
+ ospf_area,
+ lsa,
+ )
+ )
+ result = True
+ else:
+ errormsg = (
+ "[DUT: FRR] OSPF LSDB area: {} "
+ "expected Opaque-LSA is {}, Found is {}".format(
+ ospf_area, lsa, show_ospf_json
+ )
+ )
+ raise ValueError(errormsg)
+ return errormsg
+ except KeyError:
+ errormsg = "[DUT: FRR] linkLocalOpaqueLsa Not " "present"
+ return errormsg
if ospf_external_lsa:
- for lsa in ospf_external_lsa:
- try:
- for t5lsa in show_ospf_json['asExternalLinkStates']:
- if lsa['lsaId'] == t5lsa['lsaId'] and \
- lsa['advertisedRouter'] == t5lsa[
- 'advertisedRouter']:
- result = True
- break
- except KeyError:
- result = False
- if result:
- logger.info(
- "[DUT: %s] OSPF LSDB:External LSA %s",
- router, lsa)
- result = True
- else:
- errormsg = \
- "[DUT: {}] OSPF LSDB : expected" \
- " External LSA is {}".format(router, lsa)
- return errormsg
+ for lsa in ospf_external_lsa:
+ try:
+ for t5lsa in show_ospf_json["asExternalLinkStates"]:
+ if (
+ lsa["lsaId"] == t5lsa["lsaId"]
+ and lsa["advertisedRouter"] == t5lsa["advertisedRouter"]
+ ):
+ result = True
+ break
+ except KeyError:
+ result = False
+ if result:
+ logger.info("[DUT: %s] OSPF LSDB:External LSA %s", router, lsa)
+ result = True
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF LSDB : expected"
+ " External LSA is {}".format(router, lsa)
+ )
+ return errormsg
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
return result
-
-def config_ospf6_interface (tgen, topo, input_dict=None, build=False,
- load_config=True):
+def config_ospf6_interface(tgen, topo, input_dict=None, build=False, load_config=True):
"""
API to configure ospf on router.
@@ -2180,17 +2300,17 @@ def config_ospf6_interface (tgen, topo, input_dict=None, build=False,
"input_dict, passed input_dict %s", router,
str(input_dict))
continue
- ospf_data = input_dict[router]['links'][lnk]['ospf6']
+ ospf_data = input_dict[router]["links"][lnk]["ospf6"]
data_ospf_area = ospf_data.setdefault("area", None)
- data_ospf_auth = ospf_data.setdefault("authentication", None)
+ data_ospf_auth = ospf_data.setdefault("hash-algo", None)
data_ospf_dr_priority = ospf_data.setdefault("priority", None)
data_ospf_cost = ospf_data.setdefault("cost", None)
data_ospf_mtu = ospf_data.setdefault("mtu_ignore", None)
try:
- intf = topo['routers'][router]['links'][lnk]['interface']
+ intf = topo["routers"][router]["links"][lnk]["interface"]
except KeyError:
- intf = topo['switches'][router]['links'][lnk]['interface']
+ intf = topo["switches"][router]["links"][lnk]["interface"]
# interface
cmd = "interface {}".format(intf)
@@ -2201,34 +2321,50 @@ def config_ospf6_interface (tgen, topo, input_dict=None, build=False,
cmd = "ipv6 ospf area {}".format(data_ospf_area)
config_data.append(cmd)
+ # interface ospf auth
+ if data_ospf_auth:
+ cmd = "ipv6 ospf6 authentication"
+
+ if "del_action" in ospf_data:
+ cmd = "no {}".format(cmd)
+
+ if "hash-algo" in ospf_data:
+ cmd = "{} key-id {} hash-algo {} key {}".format(
+ cmd,
+ ospf_data["key-id"],
+ ospf_data["hash-algo"],
+ ospf_data["key"],
+ )
+ if "del_action" in ospf_data:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
# interface ospf dr priority
if data_ospf_dr_priority:
- cmd = "ipv6 ospf priority {}".format(
- ospf_data["priority"])
- if 'del_action' in ospf_data:
+ cmd = "ipv6 ospf priority {}".format(ospf_data["priority"])
+ if "del_action" in ospf_data:
cmd = "no {}".format(cmd)
config_data.append(cmd)
# interface ospf cost
if data_ospf_cost:
- cmd = "ipv6 ospf cost {}".format(
- ospf_data["cost"])
- if 'del_action' in ospf_data:
+ cmd = "ipv6 ospf cost {}".format(ospf_data["cost"])
+ if "del_action" in ospf_data:
cmd = "no {}".format(cmd)
config_data.append(cmd)
# interface ospf mtu
if data_ospf_mtu:
cmd = "ipv6 ospf mtu-ignore"
- if 'del_action' in ospf_data:
+ if "del_action" in ospf_data:
cmd = "no {}".format(cmd)
config_data.append(cmd)
if build:
return config_data
else:
- result = create_common_configuration(tgen, router, config_data,
- "interface_config",
- build=build)
+ result = create_common_configuration(
+ tgen, router, config_data, "interface_config", build=build
+ )
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
return result
diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py
index ade5933504..b998878118 100644
--- a/tests/topotests/lib/topogen.py
+++ b/tests/topotests/lib/topogen.py
@@ -801,8 +801,8 @@ class TopoRouter(TopoGear):
try:
return json.loads(output)
- except ValueError:
- logger.warning("vtysh_cmd: failed to convert json output")
+ except ValueError as error:
+ logger.warning("vtysh_cmd: %s: failed to convert json output: %s: %s", self.name, str(output), str(error))
return {}
def vtysh_multicmd(self, commands, pretty_output=True, daemon=None):
diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py
index d1f60bfe0d..b516a67d5c 100644
--- a/tests/topotests/lib/topotest.py
+++ b/tests/topotests/lib/topotest.py
@@ -1152,6 +1152,18 @@ class Router(Node):
self.reportCores = True
self.version = None
+ self.ns_cmd = "sudo nsenter -m -n -t {} ".format(self.pid)
+ try:
+ # Allow escaping from running inside docker
+ cgroup = open("/proc/1/cgroup").read()
+ m = re.search("[0-9]+:cpuset:/docker/([a-f0-9]+)", cgroup)
+ if m:
+ self.ns_cmd = "docker exec -it {} ".format(m.group(1)) + self.ns_cmd
+ except IOError:
+ pass
+ else:
+ logger.debug("CMD to enter {}: {}".format(self.name, self.ns_cmd))
+
def _config_frr(self, **params):
"Configure FRR binaries"
self.daemondir = params.get("frrdir")
@@ -1223,25 +1235,28 @@ class Router(Node):
dmns = rundaemons.split("\n")
# Exclude empty string at end of list
for d in dmns[:-1]:
- daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip()
- if daemonpid.isdigit() and pid_exists(int(daemonpid)):
- daemonname = os.path.basename(d.rstrip().rsplit(".", 1)[0])
- logger.info("{}: stopping {}".format(self.name, daemonname))
- try:
- os.kill(int(daemonpid), signal.SIGTERM)
- except OSError as err:
- if err.errno == errno.ESRCH:
- logger.error(
- "{}: {} left a dead pidfile (pid={})".format(
- self.name, daemonname, daemonpid
+ # Only check if daemonfilepath starts with /
+ # Avoids hang on "-> Connection closed" in above self.cmd()
+ if d[0] == '/':
+ daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip()
+ if daemonpid.isdigit() and pid_exists(int(daemonpid)):
+ daemonname = os.path.basename(d.rstrip().rsplit(".", 1)[0])
+ logger.info("{}: stopping {}".format(self.name, daemonname))
+ try:
+ os.kill(int(daemonpid), signal.SIGTERM)
+ except OSError as err:
+ if err.errno == errno.ESRCH:
+ logger.error(
+ "{}: {} left a dead pidfile (pid={})".format(
+ self.name, daemonname, daemonpid
+ )
)
- )
- else:
- logger.info(
- "{}: {} could not kill pid {}: {}".format(
- self.name, daemonname, daemonpid, str(err)
+ else:
+ logger.info(
+ "{}: {} could not kill pid {}: {}".format(
+ self.name, daemonname, daemonpid, str(err)
+ )
)
- )
if not wait:
return errors
@@ -1350,7 +1365,7 @@ class Router(Node):
term = topo_terminal if topo_terminal else "xterm"
makeTerm(self, title=title if title else cmd, term=term, cmd=cmd)
else:
- nscmd = "sudo nsenter -m -n -t {} {}".format(self.pid, cmd)
+ nscmd = self.ns_cmd + cmd
if "TMUX" in os.environ:
self.cmd("tmux select-layout main-horizontal")
wcmd = "tmux split-window -h"
@@ -1451,11 +1466,13 @@ class Router(Node):
def startRouterDaemons(self, daemons=None):
"Starts all FRR daemons for this router."
+ asan_abort = g_extra_config["asan_abort"]
gdb_breakpoints = g_extra_config["gdb_breakpoints"]
gdb_daemons = g_extra_config["gdb_daemons"]
gdb_routers = g_extra_config["gdb_routers"]
valgrind_extra = g_extra_config["valgrind_extra"]
valgrind_memleaks = g_extra_config["valgrind_memleaks"]
+ strace_daemons = g_extra_config["strace_daemons"]
bundle_data = ""
@@ -1482,7 +1499,6 @@ class Router(Node):
os.path.join(self.daemondir, "bgpd") + " -v"
).split()[2]
logger.info("{}: running version: {}".format(self.name, self.version))
-
# If `daemons` was specified then some upper API called us with
# specific daemons, otherwise just use our own configuration.
daemons_list = []
@@ -1506,13 +1522,20 @@ class Router(Node):
else:
binary = os.path.join(self.daemondir, daemon)
- cmdenv = "ASAN_OPTIONS=log_path={0}.asan".format(daemon)
+ cmdenv = "ASAN_OPTIONS="
+ if asan_abort:
+ cmdenv = "abort_on_error=1:"
+ cmdenv += "log_path={0}/{1}.{2}.asan ".format(self.logdir, self.name, daemon)
+
if valgrind_memleaks:
this_dir = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
supp_file = os.path.abspath(os.path.join(this_dir, "../../../tools/valgrind.supp"))
cmdenv += " /usr/bin/valgrind --num-callers=50 --log-file={1}/{2}.valgrind.{0}.%p --leak-check=full --suppressions={3}".format(daemon, self.logdir, self.name, supp_file)
if valgrind_extra:
cmdenv += "--gen-suppressions=all --expensive-definedness-checks=yes"
+ elif daemon in strace_daemons or "all" in strace_daemons:
+ cmdenv = "strace -f -D -o {1}/{2}.strace.{0} ".format(daemon, self.logdir, self.name)
+
cmdopt = "{} --log file:{}.log --log-level debug".format(
daemon_opts, daemon
)
diff --git a/tests/topotests/msdp_mesh_topo1/r1/pimd.conf b/tests/topotests/msdp_mesh_topo1/r1/pimd.conf
index 30cecee9e1..c2ffed4762 100644
--- a/tests/topotests/msdp_mesh_topo1/r1/pimd.conf
+++ b/tests/topotests/msdp_mesh_topo1/r1/pimd.conf
@@ -10,6 +10,7 @@ interface r1-eth1
ip igmp
!
ip pim rp 10.254.254.1
+ip pim join-prune-interval 5
ip msdp timers 10 20 3
ip msdp mesh-group mg-1 source 10.254.254.1
ip msdp mesh-group mg-1 member 10.254.254.2
diff --git a/tests/topotests/msdp_mesh_topo1/r2/pimd.conf b/tests/topotests/msdp_mesh_topo1/r2/pimd.conf
index a51c6d58c7..1719a17007 100644
--- a/tests/topotests/msdp_mesh_topo1/r2/pimd.conf
+++ b/tests/topotests/msdp_mesh_topo1/r2/pimd.conf
@@ -9,6 +9,7 @@ interface r2-eth1
ip pim
!
ip pim rp 10.254.254.2
+ip pim join-prune-interval 5
ip msdp timers 10 20 3
ip msdp mesh-group mg-1 source 10.254.254.2
ip msdp mesh-group mg-1 member 10.254.254.1
diff --git a/tests/topotests/msdp_mesh_topo1/r3/pimd.conf b/tests/topotests/msdp_mesh_topo1/r3/pimd.conf
index 663f78620e..2748a55d83 100644
--- a/tests/topotests/msdp_mesh_topo1/r3/pimd.conf
+++ b/tests/topotests/msdp_mesh_topo1/r3/pimd.conf
@@ -9,6 +9,7 @@ interface r3-eth1
ip pim
ip igmp
!
+ip pim join-prune-interval 5
ip pim rp 10.254.254.3
ip msdp timers 10 20 3
ip msdp mesh-group mg-1 source 10.254.254.3
diff --git a/tests/topotests/msdp_topo1/r1/pimd.conf b/tests/topotests/msdp_topo1/r1/pimd.conf
index fc289031f4..4274315271 100644
--- a/tests/topotests/msdp_topo1/r1/pimd.conf
+++ b/tests/topotests/msdp_topo1/r1/pimd.conf
@@ -19,3 +19,4 @@ ip msdp timers 10 20 3
ip msdp peer 192.168.0.2 source 192.168.0.1
ip msdp peer 192.168.1.2 source 192.168.1.1
ip pim rp 10.254.254.1
+ip pim join-prune-interval 5
diff --git a/tests/topotests/msdp_topo1/r2/pimd.conf b/tests/topotests/msdp_topo1/r2/pimd.conf
index ffa80b12d3..a4a69bf05c 100644
--- a/tests/topotests/msdp_topo1/r2/pimd.conf
+++ b/tests/topotests/msdp_topo1/r2/pimd.conf
@@ -15,3 +15,4 @@ ip msdp timers 10 20 3
ip msdp peer 192.168.0.1 source 192.168.0.2
ip msdp peer 192.168.2.2 source 192.168.2.1
ip pim rp 10.254.254.2
+ip pim join-prune-interval 5
diff --git a/tests/topotests/msdp_topo1/r3/pimd.conf b/tests/topotests/msdp_topo1/r3/pimd.conf
index ab12f0573a..db94447c76 100644
--- a/tests/topotests/msdp_topo1/r3/pimd.conf
+++ b/tests/topotests/msdp_topo1/r3/pimd.conf
@@ -15,3 +15,4 @@ ip msdp timers 10 20 3
ip msdp peer 192.168.1.1 source 192.168.1.2
ip msdp peer 192.168.3.2 source 192.168.3.1
ip pim rp 10.254.254.3
+ip pim join-prune-interval 5
diff --git a/tests/topotests/msdp_topo1/r4/pimd.conf b/tests/topotests/msdp_topo1/r4/pimd.conf
index b2e05cb3cb..e9bb59054c 100644
--- a/tests/topotests/msdp_topo1/r4/pimd.conf
+++ b/tests/topotests/msdp_topo1/r4/pimd.conf
@@ -19,3 +19,4 @@ ip msdp timers 10 20 3
ip msdp peer 192.168.2.1 source 192.168.2.2
ip msdp peer 192.168.3.1 source 192.168.3.2
ip pim rp 10.254.254.4
+ip pim join-prune-interval 5
diff --git a/tests/topotests/ospf_gr_topo1/__init__.py b/tests/topotests/ospf_gr_topo1/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/__init__.py
diff --git a/tests/topotests/ospf_gr_topo1/rt1/ospfd.conf b/tests/topotests/ospf_gr_topo1/rt1/ospfd.conf
new file mode 100644
index 0000000000..9c04b74d35
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt1/ospfd.conf
@@ -0,0 +1,32 @@
+password 1
+hostname rt1
+log file ospfd.log
+log commands
+!
+debug ospf zebra
+debug ospf event
+debug ospf lsa
+debug ospf te
+debug ospf packet all
+debug ospf packet ls-update detail
+debug ospf ism
+debug ospf nsm
+debug ospf nssa
+debug ospf graceful-restart
+!
+interface lo
+ ip ospf area 1
+!
+interface eth-rt2
+ ip ospf network point-to-point
+ ip ospf area 1
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+router ospf
+ router-id 1.1.1.1
+ capability opaque
+ redistribute connected
+ graceful-restart grace-period 120
+ graceful-restart helper-only
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_database.json b/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_database.json
new file mode 100644
index 0000000000..d01ac74c17
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_database.json
@@ -0,0 +1,98 @@
+{
+ "routerId":"1.1.1.1",
+ "areas":{
+ "0.0.0.1":{
+ "routerLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"1.1.1.1",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"2.2.2.2",
+ "numOfRouterLinks":2
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"2.2.2.2\/32"
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"3.3.3.3\/32"
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"4.4.4.4\/32"
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"5.5.5.5\/32"
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"6.6.6.6\/32"
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"7.7.7.7\/32"
+ },
+ {
+ "lsId":"10.0.2.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.2.0\/24"
+ },
+ {
+ "lsId":"10.0.3.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.3.0\/24"
+ },
+ {
+ "lsId":"10.0.4.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.4.0\/24"
+ },
+ {
+ "lsId":"10.0.5.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.5.0\/24"
+ },
+ {
+ "lsId":"10.0.6.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.6.0\/24"
+ }
+ ],
+ "asbrSummaryLinkStates":[
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"2.2.2.2"
+ }
+ ]
+ }
+ },
+ "asExternalLinkStates":[
+ {
+ "lsId":"172.16.1.0",
+ "advertisedRouter":"1.1.1.1",
+ "metricType":"E2",
+ "route":"172.16.1.0\/24",
+ "tag":0
+ },
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"6.6.6.6",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000000..ed290323a4
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json
@@ -0,0 +1,11 @@
+{
+ "neighbors":{
+ "2.2.2.2":[
+ {
+ "state":"Full\/DROther",
+ "address":"10.0.1.2",
+ "ifaceName":"eth-rt2:10.0.1.1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_route.json b/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_route.json
new file mode 100644
index 0000000000..548ca1e2d1
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_route.json
@@ -0,0 +1,180 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"lo"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "5.5.5.5\/32":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "6.6.6.6\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "7.7.7.7\/32":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.4.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.5.0\/24":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.6.0\/24":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "6.6.6.6":{
+ "routeType":"R ",
+ "cost":30,
+ "area":"0.0.0.1",
+ "IA":true,
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "192.168.1.0\/24":{
+ "routeType":"N E2",
+ "cost":40,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt1/show_ip_route.json b/tests/topotests/ospf_gr_topo1/rt1/show_ip_route.json
new file mode 100644
index 0000000000..3dce1eee3e
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt1/show_ip_route.json
@@ -0,0 +1,210 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "7.7.7.7\/32":[
+ {
+ "prefix":"7.7.7.7\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt1/zebra.conf b/tests/topotests/ospf_gr_topo1/rt1/zebra.conf
new file mode 100644
index 0000000000..183cd3df48
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt1/zebra.conf
@@ -0,0 +1,23 @@
+password 1
+hostname rt1
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 1.1.1.1/32
+!
+interface stub1
+ ip address 172.16.1.1/24
+!
+interface eth-rt2
+ ip address 10.0.1.1/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt2/ospfd.conf b/tests/topotests/ospf_gr_topo1/rt2/ospfd.conf
new file mode 100644
index 0000000000..922db8c8cc
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt2/ospfd.conf
@@ -0,0 +1,37 @@
+password 1
+hostname rt2
+log file ospfd.log
+log commands
+!
+debug ospf zebra
+debug ospf event
+debug ospf lsa
+debug ospf te
+debug ospf packet all
+debug ospf packet ls-update detail
+debug ospf ism
+debug ospf nsm
+debug ospf nssa
+debug ospf graceful-restart
+!
+interface lo
+ ip ospf area 0
+!
+interface eth-rt1
+ ip ospf network point-to-point
+ ip ospf area 1
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+interface eth-rt3
+ ip ospf network point-to-point
+ ip ospf area 0
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+router ospf
+ router-id 2.2.2.2
+ capability opaque
+ graceful-restart grace-period 120
+ graceful-restart helper-only
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_database.json b/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_database.json
new file mode 100644
index 0000000000..40c3e82d6a
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_database.json
@@ -0,0 +1,160 @@
+{
+ "routerId":"2.2.2.2",
+ "areas":{
+ "0.0.0.0":{
+ "routerLinkStates":[
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"2.2.2.2",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"3.3.3.3",
+ "numOfRouterLinks":7
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"4.4.4.4",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"6.6.6.6",
+ "numOfRouterLinks":3
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"1.1.1.1\/32"
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"5.5.5.5\/32"
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"7.7.7.7\/32"
+ },
+ {
+ "lsId":"10.0.1.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.1.0\/24"
+ },
+ {
+ "lsId":"10.0.5.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.5.0\/24"
+ },
+ {
+ "lsId":"10.0.6.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.6.0\/24"
+ }
+ ],
+ "asbrSummaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"2.2.2.2"
+ }
+ ]
+ },
+ "0.0.0.1":{
+ "routerLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"1.1.1.1",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"2.2.2.2",
+ "numOfRouterLinks":2
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"2.2.2.2\/32"
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"3.3.3.3\/32"
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"4.4.4.4\/32"
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"5.5.5.5\/32"
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"6.6.6.6\/32"
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"7.7.7.7\/32"
+ },
+ {
+ "lsId":"10.0.2.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.2.0\/24"
+ },
+ {
+ "lsId":"10.0.3.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.3.0\/24"
+ },
+ {
+ "lsId":"10.0.4.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.4.0\/24"
+ },
+ {
+ "lsId":"10.0.5.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.5.0\/24"
+ },
+ {
+ "lsId":"10.0.6.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.6.0\/24"
+ }
+ ],
+ "asbrSummaryLinkStates":[
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"2.2.2.2"
+ }
+ ]
+ }
+ },
+ "asExternalLinkStates":[
+ {
+ "lsId":"172.16.1.0",
+ "advertisedRouter":"1.1.1.1",
+ "metricType":"E2",
+ "route":"172.16.1.0\/24",
+ "tag":0
+ },
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"6.6.6.6",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000000..4fe92b0b98
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json
@@ -0,0 +1,18 @@
+{
+ "neighbors":{
+ "1.1.1.1":[
+ {
+ "state":"Full\/DROther",
+ "address":"10.0.1.1",
+ "ifaceName":"eth-rt1:10.0.1.2"
+ }
+ ],
+ "3.3.3.3":[
+ {
+ "state":"Full\/DROther",
+ "address":"10.0.2.3",
+ "ifaceName":"eth-rt3:10.0.2.2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_route.json b/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_route.json
new file mode 100644
index 0000000000..4accb2ba4a
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_route.json
@@ -0,0 +1,201 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "via":"eth-rt1"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"lo"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "5.5.5.5\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "6.6.6.6\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "7.7.7.7\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt1"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.4.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.5.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.6.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "1.1.1.1":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "via":"eth-rt1"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "6.6.6.6":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":10,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "via":"eth-rt1"
+ }
+ ]
+ },
+ "192.168.1.0\/24":{
+ "routeType":"N E2",
+ "cost":30,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt2/show_ip_route.json b/tests/topotests/ospf_gr_topo1/rt2/show_ip_route.json
new file mode 100644
index 0000000000..8989a45765
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt2/show_ip_route.json
@@ -0,0 +1,224 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt1"
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "7.7.7.7\/32":[
+ {
+ "prefix":"7.7.7.7\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "172.16.1.0\/24":[
+ {
+ "prefix":"172.16.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt1"
+ }
+ ]
+ }
+ ],
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt2/zebra.conf b/tests/topotests/ospf_gr_topo1/rt2/zebra.conf
new file mode 100644
index 0000000000..8bde98ad44
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt2/zebra.conf
@@ -0,0 +1,23 @@
+password 1
+hostname rt2
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 2.2.2.2/32
+!
+interface eth-rt1
+ ip address 10.0.1.2/24
+!
+interface eth-rt3
+ ip address 10.0.2.2/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt3/ospfd.conf b/tests/topotests/ospf_gr_topo1/rt3/ospfd.conf
new file mode 100644
index 0000000000..51e48f13da
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt3/ospfd.conf
@@ -0,0 +1,43 @@
+password 1
+hostname rt3
+log file ospfd.log
+log commands
+!
+debug ospf zebra
+debug ospf event
+debug ospf lsa
+debug ospf te
+debug ospf packet all
+debug ospf packet ls-update detail
+debug ospf ism
+debug ospf nsm
+debug ospf nssa
+debug ospf graceful-restart
+!
+interface lo
+ ip ospf area 0
+!
+interface eth-rt2
+ ip ospf network point-to-point
+ ip ospf area 0
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+interface eth-rt4
+ ip ospf network point-to-point
+ ip ospf area 0
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+interface eth-rt6
+ ip ospf network point-to-point
+ ip ospf area 0
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+router ospf
+ router-id 3.3.3.3
+ capability opaque
+ graceful-restart grace-period 120
+ graceful-restart helper-only
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_database.json b/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_database.json
new file mode 100644
index 0000000000..1fc5b546e4
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_database.json
@@ -0,0 +1,83 @@
+{
+ "routerId":"3.3.3.3",
+ "areas":{
+ "0.0.0.0":{
+ "routerLinkStates":[
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"2.2.2.2",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"3.3.3.3",
+ "numOfRouterLinks":7
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"4.4.4.4",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"6.6.6.6",
+ "numOfRouterLinks":3
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"1.1.1.1\/32"
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"5.5.5.5\/32"
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"7.7.7.7\/32"
+ },
+ {
+ "lsId":"10.0.1.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.1.0\/24"
+ },
+ {
+ "lsId":"10.0.5.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.5.0\/24"
+ },
+ {
+ "lsId":"10.0.6.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.6.0\/24"
+ }
+ ],
+ "asbrSummaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"2.2.2.2"
+ }
+ ]
+ }
+ },
+ "asExternalLinkStates":[
+ {
+ "lsId":"172.16.1.0",
+ "advertisedRouter":"1.1.1.1",
+ "metricType":"E2",
+ "route":"172.16.1.0\/24",
+ "tag":0
+ },
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"6.6.6.6",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000000..e3c36ab9a3
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json
@@ -0,0 +1,25 @@
+{
+ "neighbors":{
+ "2.2.2.2":[
+ {
+ "state":"Full\/DROther",
+ "address":"10.0.2.2",
+ "ifaceName":"eth-rt2:10.0.2.3"
+ }
+ ],
+ "4.4.4.4":[
+ {
+ "state":"Full\/DROther",
+ "address":"10.0.3.4",
+ "ifaceName":"eth-rt4:10.0.3.3"
+ }
+ ],
+ "6.6.6.6":[
+ {
+ "state":"Full\/DROther",
+ "address":"10.0.4.6",
+ "ifaceName":"eth-rt6:10.0.4.3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_route.json b/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_route.json
new file mode 100644
index 0000000000..b2f37e25a1
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_route.json
@@ -0,0 +1,214 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"lo"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "5.5.5.5\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "6.6.6.6\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "7.7.7.7\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.4.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt6"
+ }
+ ]
+ },
+ "10.0.5.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.6.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "1.1.1.1":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.0",
+ "IA":true,
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "6.6.6.6":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "192.168.1.0\/24":{
+ "routeType":"N E2",
+ "cost":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "via":"eth-rt6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt3/show_ip_route.json b/tests/topotests/ospf_gr_topo1/rt3/show_ip_route.json
new file mode 100644
index 0000000000..c9a1e18b92
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt3/show_ip_route.json
@@ -0,0 +1,223 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "7.7.7.7\/32":[
+ {
+ "prefix":"7.7.7.7\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "172.16.1.0\/24":[
+ {
+ "prefix":"172.16.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt3/zebra.conf b/tests/topotests/ospf_gr_topo1/rt3/zebra.conf
new file mode 100644
index 0000000000..dfd89cbe5b
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt3/zebra.conf
@@ -0,0 +1,26 @@
+password 1
+hostname rt3
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 3.3.3.3/32
+!
+interface eth-rt2
+ ip address 10.0.2.3/24
+!
+interface eth-rt4
+ ip address 10.0.3.3/24
+!
+interface eth-rt6
+ ip address 10.0.4.3/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt4/ospfd.conf b/tests/topotests/ospf_gr_topo1/rt4/ospfd.conf
new file mode 100644
index 0000000000..a54f27a1d7
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt4/ospfd.conf
@@ -0,0 +1,37 @@
+password 1
+hostname rt4
+log file ospfd.log
+log commands
+!
+debug ospf zebra
+debug ospf event
+debug ospf lsa
+debug ospf te
+debug ospf packet all
+debug ospf packet ls-update detail
+debug ospf ism
+debug ospf nsm
+debug ospf nssa
+debug ospf graceful-restart
+!
+interface lo
+ ip ospf area 0
+!
+interface eth-rt3
+ ip ospf network point-to-point
+ ip ospf area 0
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+interface eth-rt5
+ ip ospf network point-to-point
+ ip ospf area 2
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+router ospf
+ router-id 4.4.4.4
+ capability opaque
+ graceful-restart grace-period 120
+ graceful-restart helper-only
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_database.json b/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_database.json
new file mode 100644
index 0000000000..87b80414c9
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_database.json
@@ -0,0 +1,164 @@
+{
+ "routerId":"4.4.4.4",
+ "areas":{
+ "0.0.0.0":{
+ "routerLinkStates":[
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"2.2.2.2",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"3.3.3.3",
+ "numOfRouterLinks":7
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"4.4.4.4",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"6.6.6.6",
+ "numOfRouterLinks":3
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"1.1.1.1\/32"
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"5.5.5.5\/32"
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"7.7.7.7\/32"
+ },
+ {
+ "lsId":"10.0.1.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.1.0\/24"
+ },
+ {
+ "lsId":"10.0.5.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.5.0\/24"
+ },
+ {
+ "lsId":"10.0.6.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.6.0\/24"
+ }
+ ],
+ "asbrSummaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"2.2.2.2"
+ }
+ ]
+ },
+ "0.0.0.2":{
+ "routerLinkStates":[
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"4.4.4.4",
+ "numOfRouterLinks":2
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"5.5.5.5",
+ "numOfRouterLinks":3
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"1.1.1.1\/32"
+ },
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"2.2.2.2\/32"
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"3.3.3.3\/32"
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"4.4.4.4\/32"
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"6.6.6.6\/32"
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"7.7.7.7\/32"
+ },
+ {
+ "lsId":"10.0.1.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.1.0\/24"
+ },
+ {
+ "lsId":"10.0.2.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.2.0\/24"
+ },
+ {
+ "lsId":"10.0.3.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.3.0\/24"
+ },
+ {
+ "lsId":"10.0.4.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.4.0\/24"
+ },
+ {
+ "lsId":"10.0.6.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.6.0\/24"
+ }
+ ],
+ "asbrSummaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"4.4.4.4"
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"4.4.4.4"
+ }
+ ]
+ }
+ },
+ "asExternalLinkStates":[
+ {
+ "lsId":"172.16.1.0",
+ "advertisedRouter":"1.1.1.1",
+ "metricType":"E2",
+ "route":"172.16.1.0\/24",
+ "tag":0
+ },
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"6.6.6.6",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000000..2123ecb8da
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json
@@ -0,0 +1,18 @@
+{
+ "neighbors":{
+ "3.3.3.3":[
+ {
+ "state":"Full\/DROther",
+ "address":"10.0.3.3",
+ "ifaceName":"eth-rt3:10.0.3.4"
+ }
+ ],
+ "5.5.5.5":[
+ {
+ "state":"Full\/DROther",
+ "address":"10.0.5.5",
+ "ifaceName":"eth-rt5:10.0.5.4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_route.json b/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_route.json
new file mode 100644
index 0000000000..04e318aef0
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_route.json
@@ -0,0 +1,202 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"lo"
+ }
+ ]
+ },
+ "5.5.5.5\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.5",
+ "via":"eth-rt5"
+ }
+ ]
+ },
+ "6.6.6.6\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "7.7.7.7\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.4.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.5.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt5"
+ }
+ ]
+ },
+ "10.0.6.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "1.1.1.1":{
+ "routeType":"R ",
+ "cost":30,
+ "area":"0.0.0.0",
+ "IA":true,
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "6.6.6.6":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":30,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "192.168.1.0\/24":{
+ "routeType":"N E2",
+ "cost":30,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt4/show_ip_route.json b/tests/topotests/ospf_gr_topo1/rt4/show_ip_route.json
new file mode 100644
index 0000000000..8058f8f431
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt4/show_ip_route.json
@@ -0,0 +1,224 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "7.7.7.7\/32":[
+ {
+ "prefix":"7.7.7.7\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "172.16.1.0\/24":[
+ {
+ "prefix":"172.16.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt4/zebra.conf b/tests/topotests/ospf_gr_topo1/rt4/zebra.conf
new file mode 100644
index 0000000000..f399b29f3f
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt4/zebra.conf
@@ -0,0 +1,23 @@
+password 1
+hostname rt4
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 4.4.4.4/32
+!
+interface eth-rt3
+ ip address 10.0.3.4/24
+!
+interface eth-rt5
+ ip address 10.0.5.4/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt5/ospfd.conf b/tests/topotests/ospf_gr_topo1/rt5/ospfd.conf
new file mode 100644
index 0000000000..724af0e97c
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt5/ospfd.conf
@@ -0,0 +1,31 @@
+password 1
+hostname rt5
+log file ospfd.log
+log commands
+!
+debug ospf zebra
+debug ospf event
+debug ospf lsa
+debug ospf te
+debug ospf packet all
+debug ospf packet ls-update detail
+debug ospf ism
+debug ospf nsm
+debug ospf nssa
+debug ospf graceful-restart
+!
+interface lo
+ ip ospf area 2
+!
+interface eth-rt4
+ ip ospf network point-to-point
+ ip ospf area 2
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+router ospf
+ router-id 5.5.5.5
+ capability opaque
+ graceful-restart grace-period 120
+ graceful-restart helper-only
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_database.json b/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_database.json
new file mode 100644
index 0000000000..aeb8604473
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_database.json
@@ -0,0 +1,102 @@
+{
+ "routerId":"5.5.5.5",
+ "areas":{
+ "0.0.0.2":{
+ "routerLinkStates":[
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"4.4.4.4",
+ "numOfRouterLinks":2
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"5.5.5.5",
+ "numOfRouterLinks":3
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"1.1.1.1\/32"
+ },
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"2.2.2.2\/32"
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"3.3.3.3\/32"
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"4.4.4.4\/32"
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"6.6.6.6\/32"
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"7.7.7.7\/32"
+ },
+ {
+ "lsId":"10.0.1.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.1.0\/24"
+ },
+ {
+ "lsId":"10.0.2.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.2.0\/24"
+ },
+ {
+ "lsId":"10.0.3.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.3.0\/24"
+ },
+ {
+ "lsId":"10.0.4.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.4.0\/24"
+ },
+ {
+ "lsId":"10.0.6.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.6.0\/24"
+ }
+ ],
+ "asbrSummaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"4.4.4.4"
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"4.4.4.4"
+ }
+ ]
+ }
+ },
+ "asExternalLinkStates":[
+ {
+ "lsId":"172.16.1.0",
+ "advertisedRouter":"1.1.1.1",
+ "metricType":"E2",
+ "route":"172.16.1.0\/24",
+ "tag":0
+ },
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"6.6.6.6",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000000..6440b67698
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json
@@ -0,0 +1,11 @@
+{
+ "neighbors":{
+ "4.4.4.4":[
+ {
+ "state":"Full\/DROther",
+ "address":"10.0.5.4",
+ "ifaceName":"eth-rt4:10.0.5.5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_route.json b/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_route.json
new file mode 100644
index 0000000000..e7f712ea6b
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_route.json
@@ -0,0 +1,203 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "5.5.5.5\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"lo"
+ }
+ ]
+ },
+ "6.6.6.6\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "7.7.7.7\/32":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.4.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.5.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.6.0\/24":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "1.1.1.1":{
+ "routeType":"R ",
+ "cost":40,
+ "area":"0.0.0.2",
+ "IA":true,
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.2",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "6.6.6.6":{
+ "routeType":"R ",
+ "cost":30,
+ "area":"0.0.0.2",
+ "IA":true,
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":40,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "192.168.1.0\/24":{
+ "routeType":"N E2",
+ "cost":40,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt5/show_ip_route.json b/tests/topotests/ospf_gr_topo1/rt5/show_ip_route.json
new file mode 100644
index 0000000000..9896839440
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt5/show_ip_route.json
@@ -0,0 +1,225 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "7.7.7.7\/32":[
+ {
+ "prefix":"7.7.7.7\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "172.16.1.0\/24":[
+ {
+ "prefix":"172.16.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt5/zebra.conf b/tests/topotests/ospf_gr_topo1/rt5/zebra.conf
new file mode 100644
index 0000000000..49a1c05a6d
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt5/zebra.conf
@@ -0,0 +1,20 @@
+password 1
+hostname rt5
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 5.5.5.5/32
+!
+interface eth-rt4
+ ip address 10.0.5.5/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt6/ospfd.conf b/tests/topotests/ospf_gr_topo1/rt6/ospfd.conf
new file mode 100644
index 0000000000..0b9b83bcd2
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt6/ospfd.conf
@@ -0,0 +1,38 @@
+password 1
+hostname rt6
+log file ospfd.log
+log commands
+!
+debug ospf zebra
+debug ospf event
+debug ospf lsa
+debug ospf te
+debug ospf packet all
+debug ospf packet ls-update detail
+debug ospf ism
+debug ospf nsm
+debug ospf nssa
+debug ospf graceful-restart
+!
+interface lo
+ ip ospf area 0
+!
+interface eth-rt3
+ ip ospf network point-to-point
+ ip ospf area 0
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+interface eth-rt7
+ ip ospf network point-to-point
+ ip ospf area 3
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+router ospf
+ router-id 6.6.6.6
+ capability opaque
+ area 3 nssa
+ graceful-restart grace-period 120
+ graceful-restart helper-only
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_database.json b/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_database.json
new file mode 100644
index 0000000000..294b2c904e
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_database.json
@@ -0,0 +1,168 @@
+{
+ "routerId":"6.6.6.6",
+ "areas":{
+ "0.0.0.0":{
+ "routerLinkStates":[
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"2.2.2.2",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"3.3.3.3",
+ "numOfRouterLinks":7
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"4.4.4.4",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"6.6.6.6",
+ "numOfRouterLinks":3
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"1.1.1.1\/32"
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"5.5.5.5\/32"
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"7.7.7.7\/32"
+ },
+ {
+ "lsId":"10.0.1.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.1.0\/24"
+ },
+ {
+ "lsId":"10.0.5.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.5.0\/24"
+ },
+ {
+ "lsId":"10.0.6.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.6.0\/24"
+ }
+ ],
+ "asbrSummaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"2.2.2.2"
+ }
+ ]
+ },
+ "0.0.0.3":{
+ "routerLinkStates":[
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"6.6.6.6",
+ "numOfRouterLinks":2
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"7.7.7.7",
+ "numOfRouterLinks":3
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"0.0.0.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"0.0.0.0\/0"
+ },
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"1.1.1.1\/32"
+ },
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"2.2.2.2\/32"
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"3.3.3.3\/32"
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"4.4.4.4\/32"
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"5.5.5.5\/32"
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"6.6.6.6\/32"
+ },
+ {
+ "lsId":"10.0.1.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.1.0\/24"
+ },
+ {
+ "lsId":"10.0.2.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.2.0\/24"
+ },
+ {
+ "lsId":"10.0.3.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.3.0\/24"
+ },
+ {
+ "lsId":"10.0.4.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.4.0\/24"
+ },
+ {
+ "lsId":"10.0.5.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.5.0\/24"
+ }
+ ],
+ "nssaExternalLinkStates":[
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"7.7.7.7",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+ }
+ },
+ "asExternalLinkStates":[
+ {
+ "lsId":"172.16.1.0",
+ "advertisedRouter":"1.1.1.1",
+ "metricType":"E2",
+ "route":"172.16.1.0\/24",
+ "tag":0
+ },
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"6.6.6.6",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000000..d815c23927
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json
@@ -0,0 +1,18 @@
+{
+ "neighbors":{
+ "3.3.3.3":[
+ {
+ "state":"Full\/DROther",
+ "address":"10.0.4.3",
+ "ifaceName":"eth-rt3:10.0.4.6"
+ }
+ ],
+ "7.7.7.7":[
+ {
+ "state":"Full\/DROther",
+ "address":"10.0.6.7",
+ "ifaceName":"eth-rt7:10.0.6.6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_route.json b/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_route.json
new file mode 100644
index 0000000000..d9009724d5
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_route.json
@@ -0,0 +1,214 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "5.5.5.5\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "6.6.6.6\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"lo"
+ }
+ ]
+ },
+ "7.7.7.7\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.7",
+ "via":"eth-rt7"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.4.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.5.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.6.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt7"
+ }
+ ]
+ },
+ "1.1.1.1":{
+ "routeType":"R ",
+ "cost":30,
+ "area":"0.0.0.0",
+ "IA":true,
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "7.7.7.7":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.3",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.6.7",
+ "via":"eth-rt7"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":30,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "192.168.1.0\/24":{
+ "routeType":"N E2",
+ "cost":10,
+ "nexthops":[
+ {
+ "ip":"10.0.6.7",
+ "via":"eth-rt7"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt6/show_ip_route.json b/tests/topotests/ospf_gr_topo1/rt6/show_ip_route.json
new file mode 100644
index 0000000000..dd95f1fab1
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt6/show_ip_route.json
@@ -0,0 +1,224 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "7.7.7.7\/32":[
+ {
+ "prefix":"7.7.7.7\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.6.7",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt7"
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt7"
+ }
+ ]
+ }
+ ],
+ "172.16.1.0\/24":[
+ {
+ "prefix":"172.16.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.6.7",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt7"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt6/zebra.conf b/tests/topotests/ospf_gr_topo1/rt6/zebra.conf
new file mode 100644
index 0000000000..d6a8f52b3a
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt6/zebra.conf
@@ -0,0 +1,23 @@
+password 1
+hostname rt6
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 6.6.6.6/32
+!
+interface eth-rt3
+ ip address 10.0.4.6/24
+!
+interface eth-rt7
+ ip address 10.0.6.6/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt7/ospfd.conf b/tests/topotests/ospf_gr_topo1/rt7/ospfd.conf
new file mode 100644
index 0000000000..49db254410
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt7/ospfd.conf
@@ -0,0 +1,33 @@
+password 1
+hostname rt7
+log file ospfd.log
+log commands
+!
+debug ospf zebra
+debug ospf event
+debug ospf lsa
+debug ospf te
+debug ospf packet all
+debug ospf packet ls-update detail
+debug ospf ism
+debug ospf nsm
+debug ospf nssa
+debug ospf graceful-restart
+!
+interface lo
+ ip ospf area 3
+!
+interface eth-rt6
+ ip ospf network point-to-point
+ ip ospf area 3
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+router ospf
+ router-id 7.7.7.7
+ capability opaque
+ redistribute connected
+ area 3 nssa
+ graceful-restart grace-period 120
+ graceful-restart helper-only
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_database.json b/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_database.json
new file mode 100644
index 0000000000..4916fba9d4
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_database.json
@@ -0,0 +1,99 @@
+{
+ "routerId":"7.7.7.7",
+ "areas":{
+ "0.0.0.3":{
+ "routerLinkStates":[
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"6.6.6.6",
+ "numOfRouterLinks":2
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"7.7.7.7",
+ "numOfRouterLinks":3
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"0.0.0.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"0.0.0.0\/0"
+ },
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"1.1.1.1\/32"
+ },
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"2.2.2.2\/32"
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"3.3.3.3\/32"
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"4.4.4.4\/32"
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"5.5.5.5\/32"
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"6.6.6.6\/32"
+ },
+ {
+ "lsId":"10.0.1.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.1.0\/24"
+ },
+ {
+ "lsId":"10.0.2.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.2.0\/24"
+ },
+ {
+ "lsId":"10.0.3.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.3.0\/24"
+ },
+ {
+ "lsId":"10.0.4.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.4.0\/24"
+ },
+ {
+ "lsId":"10.0.5.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.5.0\/24"
+ }
+ ],
+ "nssaExternalLinkStates":[
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"7.7.7.7",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+ }
+ },
+ "asExternalLinkStates":[
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"7.7.7.7",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000000..2254aea9a6
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json
@@ -0,0 +1,11 @@
+{
+ "neighbors":{
+ "6.6.6.6":[
+ {
+ "state":"Full\/DROther",
+ "address":"10.0.6.6",
+ "ifaceName":"eth-rt6:10.0.6.7"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_route.json b/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_route.json
new file mode 100644
index 0000000000..89bad320bb
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_route.json
@@ -0,0 +1,168 @@
+{
+ "0.0.0.0\/0":{
+ "routeType":"N IA",
+ "cost":11,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "5.5.5.5\/32":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "6.6.6.6\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "7.7.7.7\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"lo"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "10.0.4.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "10.0.5.0\/24":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "10.0.6.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt6"
+ }
+ ]
+ },
+ "6.6.6.6":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.3",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt7/show_ip_route.json b/tests/topotests/ospf_gr_topo1/rt7/show_ip_route.json
new file mode 100644
index 0000000000..0fb906b76b
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt7/show_ip_route.json
@@ -0,0 +1,210 @@
+{
+ "0.0.0.0\/0":[
+ {
+ "prefix":"0.0.0.0\/0",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":11,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "7.7.7.7\/32":[
+ {
+ "prefix":"7.7.7.7\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt7/zebra.conf b/tests/topotests/ospf_gr_topo1/rt7/zebra.conf
new file mode 100644
index 0000000000..c481e4532b
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt7/zebra.conf
@@ -0,0 +1,23 @@
+password 1
+hostname rt7
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 7.7.7.7/32
+!
+interface stub1
+ ip address 192.168.1.1/24
+!
+interface eth-rt6
+ ip address 10.0.6.7/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py b/tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py
new file mode 100755
index 0000000000..0507c2d516
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py
@@ -0,0 +1,393 @@
+#!/usr/bin/env python
+
+#
+# test_ospf_gr_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_ospf_gr_topo1.py:
+
+ +---------+
+ | RT1 |
+ | 1.1.1.1 |
+ +---------+
+ |eth-rt2
+ |
+ |10.0.1.0/24
+ |
+ |eth-rt1
+ +---------+
+ | RT2 |
+ | 2.2.2.2 |
+ +---------+
+ |eth-rt3
+ |
+ |10.0.2.0/24
+ |
+ |eth-rt2
+ +---------+
+ | RT3 |
+ | 3.3.3.3 |
+ +---------+
+ eth-rt4| |eth-rt6
+ | |
+ 10.0.3.0/24 | | 10.0.4.0/24
+ +---------+ +--------+
+ | |
+ |eth-rt3 |eth-rt3
+ +---------+ +---------+
+ | RT4 | | RT6 |
+ | 4.4.4.4 | | 6.6.6.6 |
+ +---------+ +---------+
+ |eth-rt5 |eth-rt7
+ | |
+ |10.0.5.0/24 |10.0.6.0/24
+ | |
+ |eth-rt4 |eth-rt6
+ +---------+ +---------+
+ | RT5 | | RT7 |
+ | 5.5.5.5 | | 7.7.7.7 |
+ +---------+ +---------+
+"""
+
+import os
+import sys
+import pytest
+import json
+import re
+import tempfile
+from time import sleep
+from functools import partial
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from lib.common_config import (
+ kill_router_daemons,
+ start_router_daemons,
+)
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+pytestmark = [pytest.mark.ospfd]
+
+# Global multi-dimensional dictionary containing all expected outputs
+outputs = {}
+
+
+class TemplateTopo(Topo):
+ "Test topology builder"
+
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ #
+ # Define FRR Routers
+ #
+ for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-rt2")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt1")
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["rt1"], nodeif="stub1")
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt3")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt2")
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt4")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt3")
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt6")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt3")
+
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4")
+
+ switch = tgen.add_switch("s7")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt7")
+ switch.add_link(tgen.gears["rt7"], nodeif="eth-rt6")
+
+ switch = tgen.add_switch("s8")
+ switch.add_link(tgen.gears["rt7"], nodeif="stub1")
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(TemplateTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def router_compare_json_output(rname, command, reference, tries):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
+ _, diff = topotest.run_and_expect(test_func, None, count=tries, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+def check_routers(initial_convergence=False, exiting=None, restarting=None):
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]:
+ # Check the RIB first, which should be preserved across restarts in
+ # all routers of the routing domain.
+ if initial_convergence == True:
+ tries = 240
+ else:
+ tries = 1
+ router_compare_json_output(
+ rname, "show ip route ospf json", "show_ip_route.json", tries
+ )
+
+ # Check that all adjacencies are up and running (except when there's
+ # an OSPF instance that is shutting down).
+ if exiting == None:
+ tries = 240
+ router_compare_json_output(
+ rname, "show ip ospf neighbor json", "show_ip_ospf_neighbor.json", tries
+ )
+
+ # Check the OSPF RIB and LSDB.
+ # In the restarting router, wait up to one minute for the LSDB to converge.
+ if exiting != rname:
+ if initial_convergence == True or restarting == rname:
+ tries = 240
+ else:
+ tries = 1
+ router_compare_json_output(
+ rname, "show ip ospf database json", "show_ip_ospf_database.json", tries
+ )
+ router_compare_json_output(
+ rname, "show ip ospf route json", "show_ip_ospf_route.json", tries
+ )
+
+
+#
+# Test initial network convergence
+#
+def test_initial_convergence():
+ logger.info("Test: verify initial network convergence")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_routers(initial_convergence=True)
+
+
+#
+# Test rt1 performing a graceful restart
+#
+def test_gr_rt1():
+ logger.info("Test: verify rt1 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt1"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
+ sleep(3)
+ kill_router_daemons(tgen, "rt1", ["ospfd"], save_config=False)
+ check_routers(exiting="rt1")
+
+ start_router_daemons(tgen, "rt1", ["ospfd"])
+ check_routers(restarting="rt1")
+
+
+#
+# Test rt2 performing a graceful restart
+#
+def test_gr_rt2():
+ logger.info("Test: verify rt2 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt2"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
+ sleep(3)
+ kill_router_daemons(tgen, "rt2", ["ospfd"], save_config=False)
+ check_routers(exiting="rt2")
+
+ start_router_daemons(tgen, "rt2", ["ospfd"])
+ check_routers(restarting="rt2")
+
+
+#
+# Test rt3 performing a graceful restart
+#
+def test_gr_rt3():
+ logger.info("Test: verify rt3 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt3"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
+ sleep(3)
+ kill_router_daemons(tgen, "rt3", ["ospfd"], save_config=False)
+ check_routers(exiting="rt3")
+
+ start_router_daemons(tgen, "rt3", ["ospfd"])
+ check_routers(restarting="rt3")
+
+
+#
+# Test rt4 performing a graceful restart
+#
+def test_gr_rt4():
+ logger.info("Test: verify rt4 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt4"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
+ sleep(3)
+ kill_router_daemons(tgen, "rt4", ["ospfd"], save_config=False)
+ check_routers(exiting="rt4")
+
+ start_router_daemons(tgen, "rt4", ["ospfd"])
+ check_routers(restarting="rt4")
+
+
+#
+# Test rt5 performing a graceful restart
+#
+def test_gr_rt5():
+ logger.info("Test: verify rt5 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt5"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
+ sleep(3)
+ kill_router_daemons(tgen, "rt5", ["ospfd"], save_config=False)
+ check_routers(exiting="rt5")
+
+ start_router_daemons(tgen, "rt5", ["ospfd"])
+ check_routers(restarting="rt5")
+
+
+#
+# Test rt6 performing a graceful restart
+#
+def test_gr_rt6():
+ logger.info("Test: verify rt6 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt6"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
+ sleep(3)
+ kill_router_daemons(tgen, "rt6", ["ospfd"], save_config=False)
+ check_routers(exiting="rt6")
+
+ start_router_daemons(tgen, "rt6", ["ospfd"])
+ check_routers(restarting="rt6")
+
+
+#
+# Test rt7 performing a graceful restart
+#
+def test_gr_rt7():
+ logger.info("Test: verify rt7 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt7"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
+ sleep(3)
+ kill_router_daemons(tgen, "rt7", ["ospfd"], save_config=False)
+ check_routers(exiting="rt7")
+
+ start_router_daemons(tgen, "rt7", ["ospfd"])
+ check_routers(restarting="rt7")
+
+
+# Memory leak test template
+def test_memory_leak():
+ "Run the memory leak test and report results."
+ tgen = get_topogen()
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp.json
new file mode 100644
index 0000000000..c928093925
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp.json
@@ -0,0 +1,347 @@
+{
+ "address_types": [
+ "ipv6"
+ ],
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link4": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link5": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link6": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link7": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r1-link1": {
+ "nbr": "r1"
+ },
+ "r1-link2": {
+ "nbr": "r1"
+ },
+ "r1-link3": {
+ "nbr": "r1"
+ },
+ "r1-link4": {
+ "nbr": "r1"
+ },
+ "r1-link5": {
+ "nbr": "r1"
+ },
+ "r1-link6": {
+ "nbr": "r1"
+ },
+ "r1-link7": {
+ "nbr": "r1"
+ },
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link4": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link5": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link6": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link7": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r0-link1": {
+ "nbr": "r0"
+ },
+ "r0-link2": {
+ "nbr": "r0"
+ },
+ "r0-link3": {
+ "nbr": "r0"
+ },
+ "r0-link4": {
+ "nbr": "r0"
+ },
+ "r0-link5": {
+ "nbr": "r0"
+ },
+ "r0-link6": {
+ "nbr": "r0"
+ },
+ "r0-link7": {
+ "nbr": "r0"
+ },
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR1",
+ "ospf": {
+ "area": "0.0.0.0"
+ },
+ "ospf6": {
+ "area": "0.0.0.0"
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_routemaps.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_routemaps.json
new file mode 100644
index 0000000000..226f84f320
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_routemaps.json
@@ -0,0 +1,137 @@
+{
+ "address_types": ["ipv6"],
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {"ipv6": "fd00::", "v6mask": 64},
+ "lo_prefix": {"ipv6": "2001:db8:f::", "v6mask": 128},
+ "routers": {
+ "r0": {
+ "links": {
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.0",
+ "neighbors": {"r1": {}, "r2": {}, "r3": {}}
+ }
+ },
+ "r1": {
+ "links": {
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.1",
+ "neighbors": {"r0": {}, "r2": {}, "r3": {}}
+ }
+ },
+ "r2": {
+ "links": {
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.2",
+ "neighbors": {"r1": {}, "r0": {}, "r3": {}}
+ }
+ },
+ "r3": {
+ "links": {
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.3",
+ "neighbors": {"r0": {}, "r1": {}, "r2": {}}
+ }
+ }
+ }
+}
diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py
new file mode 100644
index 0000000000..a439375be8
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py
@@ -0,0 +1,520 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+import json
+from copy import deepcopy
+from ipaddress import IPv4Address
+from lib.topotest import frr_unicode
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from mininet.topo import Topo
+from lib.topogen import Topogen, get_topogen
+import ipaddress
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ step,
+ create_route_maps,
+ shutdown_bringup_interface,
+ create_interfaces_cfg,
+ topo_daemons,
+ get_frr_ipv6_linklocal,
+)
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+from lib.ospf import (
+ verify_ospf6_neighbor,
+ config_ospf_interface,
+ clear_ospf,
+ verify_ospf6_rib,
+ create_router_ospf,
+ verify_ospf6_interface,
+ verify_ospf6_database,
+ config_ospf6_interface,
+)
+
+from ipaddress import IPv6Address
+
+# Global variables
+topo = None
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/ospfv3_ecmp.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ],
+ "ipv6": ["2::1/128", "2::2/128", "2::3/128", "2::4/128", "2::5/128"],
+}
+"""
+TOPOLOGY :
+ Please view in a fixed-width font such as Courier.
+ +---+ A1 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A2
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A3 +---+
+
+TESTCASES :
+1. Verify OSPF ECMP with max path configured as 8 (ECMPconfigured at FRR level)
+2. Verify OSPF ECMP with max path configured as 2 (Edge having 2 uplink ports)
+ """
+
+
+class CreateTopo(Topo):
+ """
+ Test topology builder.
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function."""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ global topo
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+def red_static(dut, config=True):
+ """Local def for Redstribute static routes inside ospf."""
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf6": {"redistribute": [{"redist_type": "static"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf6": {"redistribute": [{"redist_type": "static", "delete": True}]}
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+
+def red_connected(dut, config=True):
+ """Local def for Redstribute connected routes inside ospf."""
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf6": {"redistribute": [{"redist_type": "connected"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf6": {
+ "redistribute": [{"redist_type": "connected", "del_action": True}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase: Failed \n Error: {}".format(result)
+
+
+def get_llip(onrouter, intf):
+ """
+ API to get the link local ipv6 address of a perticular interface
+
+ Parameters
+ ----------
+ * `fromnode`: Source node
+ * `tonode` : interface for which link local ip needs to be returned.
+
+ Usage
+ -----
+ result = get_llip('r1', 'r2-link0')
+
+ Returns
+ -------
+ 1) link local ipv6 address from the interface.
+ 2) errormsg - when link local ip not found.
+ """
+ tgen = get_topogen()
+ intf = topo["routers"][onrouter]["links"][intf]["interface"]
+ llip = get_frr_ipv6_linklocal(tgen, onrouter, intf)
+ if llip:
+ logger.info("llip ipv6 address to be set as NH is %s", llip)
+ return llip
+ return None
+
+
+def get_glipv6(onrouter, intf):
+ """
+ API to get the global ipv6 address of a perticular interface
+
+ Parameters
+ ----------
+ * `onrouter`: Source node
+ * `intf` : interface for which link local ip needs to be returned.
+
+ Usage
+ -----
+ result = get_glipv6('r1', 'r2-link0')
+
+ Returns
+ -------
+ 1) global ipv6 address from the interface.
+ 2) errormsg - when link local ip not found.
+ """
+ glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0]
+ if glipv6:
+ logger.info("Global ipv6 address to be set as NH is %s", glipv6)
+ return glipv6
+ return None
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospfv3_ecmp_tc16_p0(request):
+ """
+ Verify OSPF ECMP.
+
+ Verify OSPF ECMP with max path configured as 8 (ECMP
+ configured at FRR level)
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ step("Configure 8 interfaces between R1 and R2 and enable ospf in area 0.")
+
+ reset_config_on_routers(tgen)
+
+ step("Verify that OSPF is up with 8 neighborship sessions.")
+ dut = "r1"
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Configure a static route in R0 and redistribute in OSPF.")
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ llip = get_llip("r0", "r1-link1")
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that route in R2 in stalled with 8 next hops.")
+ nh = []
+ for item in range(1, 7):
+ nh.append(llip)
+
+ llip = get_llip("r0", "r1")
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ nh2 = llip
+
+ nh.append(nh2)
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("shut no shut all the interfaces on the remote router - R2")
+ dut = "r1"
+ for intfr in range(1, 7):
+ intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route present in OSPF RIB. Error: {}".format(
+ tc_name, result
+ )
+
+ protocol = "ospf"
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route present in RIB. Error: {}".format(tc_name, result)
+
+ for intfr in range(1, 7):
+ intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("shut no shut on all the interfaces on DUT (r1)")
+ for intfr in range(1, 7):
+ intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ for intfr in range(1, 7):
+ intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "Verify that all the neighbours are up and routes are installed"
+ " with 8 next hop in ospf and ip route tables on R1."
+ )
+
+ step("Verify that OSPF is up with 8 neighborship sessions.")
+ dut = "r1"
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_ecmp_tc17_p0(request):
+ """
+ Verify OSPF ECMP.
+
+ Verify OSPF ECMP with max path configured as 2 (Edge having 2 uplink ports)
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ step("Configure 2 interfaces between R1 and R2 & enable ospf in area 0.")
+
+ reset_config_on_routers(tgen)
+
+ step("Verify that OSPF is up with 2 neighborship sessions.")
+ dut = "r1"
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Configure a static route in R0 and redistribute in OSPF.")
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that route in R2 in stalled with 2 next hops.")
+
+ llip = get_llip("r0", "r1-link1")
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ nh1 = llip
+
+ llip = get_llip("r0", "r1")
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ nh2 = llip
+
+ nh = [nh1, nh2]
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure ECMP value as 1.")
+ max_path = {"r1": {"ospf6": {"maximum-paths": 1}}}
+ result = create_router_ospf(tgen, topo, max_path)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ max_path = {"r1": {"ospf6": {"maximum-paths": 2}}}
+ result = create_router_ospf(tgen, topo, max_path)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure cost on R0 as 100")
+ r0_ospf_cost = {"r0": {"links": {"r1": {"ospf6": {"cost": 100}}}}}
+ result = config_ospf6_interface(tgen, topo, r0_ospf_cost)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py
new file mode 100644
index 0000000000..9ca460e487
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py
@@ -0,0 +1,872 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2021 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+import json
+from copy import deepcopy
+from ipaddress import IPv4Address
+from lib.topotest import frr_unicode
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from mininet.topo import Topo
+from lib.topogen import Topogen, get_topogen
+import ipaddress
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ create_prefix_lists,
+ verify_rib,
+ create_static_routes,
+ step,
+ create_route_maps,
+ verify_prefix_lists,
+ get_frr_ipv6_linklocal,
+ topo_daemons,
+)
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+from lib.ospf import (
+ verify_ospf6_neighbor,
+ config_ospf_interface,
+ clear_ospf,
+ verify_ospf6_rib,
+ create_router_ospf,
+ verify_ospf6_interface,
+ verify_ospf6_database,
+ config_ospf6_interface,
+)
+
+from ipaddress import IPv6Address
+
+# Global variables
+topo = None
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/ospfv3_routemaps.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ],
+ "ipv6": ["2::1/128", "2::2/128", "2::3/128", "2::4/128", "2::5/128"],
+}
+
+routerids = ["100.1.1.0", "100.1.1.1", "100.1.1.2", "100.1.1.3"]
+
+"""
+TOPOOLOGY =
+ Please view in a fixed-width font such as Courier.
+ +---+ A1 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A2
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A3 +---+
+
+TESTCASES =
+2. Verify OSPF route map support functionality when route map is not
+ configured at system level but configured in OSPF
+4. Verify OSPF route map support functionality
+ when route map actions are toggled.
+5. Verify OSPF route map support functionality with multiple sequence
+ numbers in a single route-map for different match/set clauses.
+6. Verify OSPF route map support functionality when we add/remove route-maps
+ with multiple set clauses and without any match statement.(Set only)
+7. Verify OSPF route map support functionality when we
+ add/remove route-maps with multiple match clauses and without
+ any set statement.(Match only)
+8. Verify OSPF route map applied to ospf redistribution with ipv6 prefix list
+ """
+
+
+class CreateTopo(Topo):
+ """
+ Test topology builder.
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function."""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ global topo
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospfv3_routemaps_functionality_tc20_p0(request):
+ """
+ OSPF route map support functionality.
+
+ Verify OSPF route map support functionality when route map is not
+ configured at system level but configured in OSPF
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+
+ reset_config_on_routers(tgen)
+
+ step("Create static routes(10.0.20.1/32 and 10.0.20.2/32) in R0")
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Redistribute to ospf using route map ( non existent route map)")
+ ospf_red_r1 = {
+ "r0": {
+ "ospf6": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv6"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that routes are not allowed in OSPF even tough no "
+ "matching routing map is configured."
+ )
+
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "configure the route map with the same name that is used "
+ "in the ospf with deny rule."
+ )
+
+ # Create route map
+ routemaps = {"r0": {"route_maps": {"rmap_ipv6": [{"action": "deny"}]}}}
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that now route map is activated & routes are denied in OSPF.")
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ routemaps = {"r0": {"route_maps": {"rmap_ipv6": [{"action": "deny"}]}}}
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that now route map is activated & routes are denied in OSPF.")
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ step("Delete the route map.")
+ # Create route map
+ routemaps = {
+ "r0": {"route_maps": {"rmap_ipv6": [{"action": "deny", "delete": True}]}}
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that routes are allowed in OSPF even tough "
+ "no matching routing map is configured."
+ )
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_routemaps_functionality_tc25_p0(request):
+ """
+ OSPF route map support functionality.
+
+ Verify OSPF route map support functionality
+ when route map actions are toggled.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+
+ reset_config_on_routers(tgen)
+
+ step(
+ "Create static routes(10.0.20.1/32) in R1 and redistribute "
+ "to OSPF using route map."
+ )
+
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r0 = {
+ "r0": {
+ "ospf6": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv6"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step("Configure route map with permit rule")
+ # Create route map
+ routemaps = {"r0": {"route_maps": {"rmap_ipv6": [{"action": "permit"}]}}}
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that route is advertised to R1.")
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step("Configure route map with deny rule")
+ # Create route map
+ routemaps = {
+ "r0": {"route_maps": {"rmap_ipv6": [{"seq_id": 10, "action": "deny"}]}}
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Verify that route is not advertised to R1.")
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_routemaps_functionality_tc22_p0(request):
+ """
+ OSPF Route map - Multiple sequence numbers.
+
+ Verify OSPF route map support functionality with multiple sequence
+ numbers in a single route-map for different match/set clauses.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+
+ reset_config_on_routers(tgen)
+
+ step(
+ "Configure route map with seq number 10 to with ip prefix"
+ " permitting route 10.0.20.1/32 in R1"
+ )
+ step(
+ "Configure route map with seq number 20 to with ip prefix"
+ " permitting route 10.0.20.2/32 in R1"
+ )
+
+ # Create route map
+ input_dict_3 = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "match": {"ipv6": {"prefix_lists": "pf_list_1_ipv6"}},
+ },
+ {
+ "action": "permit",
+ "seq_id": "20",
+ "match": {"ipv6": {"prefix_lists": "pf_list_2_ipv4"}},
+ },
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv6": [
+ {"seqid": 10, "network": NETWORK["ipv6"][0], "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_2_ipv4": [
+ {"seqid": 10, "network": NETWORK["ipv6"][1], "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure static routes 10.0.20.1/32 and 10.0.20.2 in R1")
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure redistribute static route with route map.")
+ ospf_red_r0 = {
+ "r0": {
+ "ospf6": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv6"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 2,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that both routes are learned in R1 and R2")
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r2"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Change route map with seq number 20 to deny.")
+ # Create route map
+ input_dict_3 = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [
+ {
+ "action": "deny",
+ "seq_id": "20",
+ "match": {"ipv6": {"prefix_lists": "pf_list_2_ipv4"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify the route 10.0.20.2/32 is withdrawn and not present "
+ "in the routing table of R0 and R1."
+ )
+
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv6"][1], "next_hop": "Null0"}]}
+ }
+
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_routemaps_functionality_tc24_p0(request):
+ """
+ OSPF Route map - Multiple set clauses.
+
+ Verify OSPF route map support functionality when we
+ add/remove route-maps with multiple match clauses and without
+ any set statement.(Match only)
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+
+ reset_config_on_routers(tgen)
+
+ step(
+ "Create static routes(10.0.20.1/32) in R1 and redistribute to "
+ "OSPF using route map."
+ )
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 1,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r0 = {
+ "r0": {
+ "ospf6": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv6"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that prefix-list is created in R0.")
+ result = verify_prefix_lists(tgen, pfx_list)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [
+ {
+ "action": "permit",
+ "match": {"ipv6": {"prefix_lists": "pf_list_1_ipv6"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that metric falls back to original metric for ospf routes.")
+ dut = "r1"
+ protocol = "ospf"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Create static routes(10.0.20.1/32) in R1 and redistribute to "
+ "OSPF using route map."
+ )
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][1],
+ "no_of_ip": 1,
+ "next_hop": "Null0",
+ "tag": 1000,
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that prefix-list is created in R0.")
+ result = verify_prefix_lists(tgen, pfx_list)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [{"action": "permit", "match": {"ipv6": {"tag": "1000"}}}]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that metric falls back to original metric for ospf routes.")
+ dut = "r1"
+ protocol = "ospf"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the match clause with tag in route map")
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [
+ {
+ "action": "permit",
+ "match": {"ipv6": {"tag": "1000", "delete": True}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that metric falls back to original metric for ospf routes.")
+ dut = "r1"
+ protocol = "ospf"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the match clause with metric in route map.")
+
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [
+ {
+ "action": "permit",
+ "match": {"ipv6": {"prefix_lists": "pf_list_1_ipv6"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py
index 4aa71bfb16..e01c6d6047 100644
--- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py
@@ -281,6 +281,233 @@ def red_connected(dut, config=True):
# ##################################
# Test cases start here.
# ##################################
+def test_ospfv3_redistribution_tc5_p0(request):
+ """Test OSPF intra area route calculations."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+
+ step("Verify that OSPF neighbors are FULL.")
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("verify intra area route is calculated for r0-r3 interface ip in R1")
+ ip = topo["routers"]["r0"]["links"]["r3"]["ipv6"]
+ ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network)
+
+ llip = get_llip("r0", "r1")
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, llip)
+
+ nh = llip
+ input_dict = {
+ "r1": {"static_routes": [{"network": ip_net, "no_of_ip": 1, "routeType": "N"}]}
+ }
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the ip address on newly configured loopback of R0")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv6": topo["routers"]["r0"]["links"]["r3"]["ipv6"],
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route present in RIB. Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route present in RIB. Error: {}".format(tc_name, result)
+
+ step("Add back the deleted ip address on newly configured interface of R0")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv6": topo["routers"]["r0"]["links"]["r3"]["ipv6"],
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Shut no shut interface on R0")
+ dut = "r0"
+ intf = topo["routers"]["r0"]["links"]["r3"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("un shut the OSPF interface on R0")
+ dut = "r0"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_redistribution_tc6_p0(request):
+ """Test OSPF inter area route calculations."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+
+ step("Verify that OSPF neighbors are FULL.")
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("verify intra area route is calculated for r0-r3 interface ip in R1")
+ ip = topo["routers"]["r0"]["links"]["r3"]["ipv6"]
+ ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network)
+ llip = get_llip("r0", "r1")
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, llip)
+ nh = llip
+ input_dict = {
+ "r1": {"static_routes": [{"network": ip_net, "no_of_ip": 1, "routeType": "N"}]}
+ }
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the ip address on newly configured loopback of R0")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv6": topo["routers"]["r0"]["links"]["r3"]["ipv6"],
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route present in RIB. Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route present in RIB. Error: {}".format(tc_name, result)
+
+ step("Add back the deleted ip address on newly configured interface of R0")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv6": topo["routers"]["r0"]["links"]["r3"]["ipv6"],
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Shut no shut interface on R0")
+ dut = "r0"
+ intf = topo["routers"]["r0"]["links"]["r3"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("Verify that intraroute calculated for R1 intf on R0 is deleted.")
+ dut = "r1"
+
+ step("un shut the OSPF interface on R0")
+ dut = "r0"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
def test_ospfv3_cost_tc52_p0(request):
"""OSPF Cost - verifying ospf interface cost functionality"""
tc_name = request.node.name
@@ -368,7 +595,6 @@ def test_ospfv3_cost_tc52_p0(request):
write_test_footer(tc_name)
-
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py
index a84f1a1eb6..faae4b3e17 100644
--- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py
@@ -54,7 +54,7 @@ from lib.common_config import (
create_route_maps,
shutdown_bringup_interface,
create_interfaces_cfg,
- topo_daemons,
+ topo_daemons
)
from lib.topolog import logger
from lib.topojson import build_topo_from_json, build_config_from_json
diff --git a/tests/topotests/pim_acl/h1/zebra.conf b/tests/topotests/pim_acl/h1/zebra.conf
new file mode 100644
index 0000000000..3d6540d40c
--- /dev/null
+++ b/tests/topotests/pim_acl/h1/zebra.conf
@@ -0,0 +1,10 @@
+!
+hostname h1
+log file zebra.log
+!
+interface h1-eth0
+ description connection to r1 via sw1
+ ip address 192.168.100.10/24
+!
+ip route 0.0.0.0/0 192.168.100.1
+!
diff --git a/tests/topotests/pim_acl/h2/zebra.conf b/tests/topotests/pim_acl/h2/zebra.conf
new file mode 100644
index 0000000000..95342f9e8a
--- /dev/null
+++ b/tests/topotests/pim_acl/h2/zebra.conf
@@ -0,0 +1,8 @@
+hostname h2
+!
+interface h2-eth0
+ description connection to r1 via sw2
+ ip address 192.168.101.2/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_acl/r1/acl_1_pim_join.json b/tests/topotests/pim_acl/r1/acl_1_pim_join.json
new file mode 100644
index 0000000000..1b44b2b5cf
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/acl_1_pim_join.json
@@ -0,0 +1,21 @@
+{
+ "r1-eth0":{
+ "name":"r1-eth0",
+ "state":"up",
+ "address":"192.168.100.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.1":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.1",
+ "upTime":"--:--:--",
+ "expire":"--:--",
+ "prune":"--:--",
+ "channelJoinName":"NOINFO",
+ "protocolIgmp":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r1/acl_2_pim_join.json b/tests/topotests/pim_acl/r1/acl_2_pim_join.json
new file mode 100644
index 0000000000..c020a489a9
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/acl_2_pim_join.json
@@ -0,0 +1,21 @@
+{
+ "r1-eth0":{
+ "name":"r1-eth0",
+ "state":"up",
+ "address":"192.168.100.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.17":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.17",
+ "upTime":"--:--:--",
+ "expire":"--:--",
+ "prune":"--:--",
+ "channelJoinName":"NOINFO",
+ "protocolIgmp":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r1/acl_3_pim_join.json b/tests/topotests/pim_acl/r1/acl_3_pim_join.json
new file mode 100644
index 0000000000..6122f73992
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/acl_3_pim_join.json
@@ -0,0 +1,21 @@
+{
+ "r1-eth0":{
+ "name":"r1-eth0",
+ "state":"up",
+ "address":"192.168.100.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.32":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.32",
+ "upTime":"--:--:--",
+ "expire":"--:--",
+ "prune":"--:--",
+ "channelJoinName":"NOINFO",
+ "protocolIgmp":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r1/acl_4_pim_join.json b/tests/topotests/pim_acl/r1/acl_4_pim_join.json
new file mode 100644
index 0000000000..5f72256ba7
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/acl_4_pim_join.json
@@ -0,0 +1,21 @@
+{
+ "r1-eth0":{
+ "name":"r1-eth0",
+ "state":"up",
+ "address":"192.168.100.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.255":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.255",
+ "upTime":"--:--:--",
+ "expire":"--:--",
+ "prune":"--:--",
+ "channelJoinName":"NOINFO",
+ "protocolIgmp":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r1/acl_5_pim_join.json b/tests/topotests/pim_acl/r1/acl_5_pim_join.json
new file mode 100644
index 0000000000..70021bdbec
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/acl_5_pim_join.json
@@ -0,0 +1,21 @@
+{
+ "r1-eth0":{
+ "name":"r1-eth0",
+ "state":"up",
+ "address":"192.168.100.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.97":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.97",
+ "upTime":"--:--:--",
+ "expire":"--:--",
+ "prune":"--:--",
+ "channelJoinName":"NOINFO",
+ "protocolIgmp":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r1/acl_6_pim_join.json b/tests/topotests/pim_acl/r1/acl_6_pim_join.json
new file mode 100644
index 0000000000..2baac6cb22
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/acl_6_pim_join.json
@@ -0,0 +1,21 @@
+{
+ "r1-eth0":{
+ "name":"r1-eth0",
+ "state":"up",
+ "address":"192.168.100.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.70":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.70",
+ "upTime":"--:--:--",
+ "expire":"--:--",
+ "prune":"--:--",
+ "channelJoinName":"NOINFO",
+ "protocolIgmp":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r1/ospf_neighbor.json b/tests/topotests/pim_acl/r1/ospf_neighbor.json
new file mode 100644
index 0000000000..a8fc093e90
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/ospf_neighbor.json
@@ -0,0 +1,59 @@
+{
+ "neighbors":{
+ "192.168.0.11":[
+ {
+ "priority":10,
+ "state":"Full\/Backup",
+ "address":"192.168.101.11",
+ "ifaceName":"r1-eth1:192.168.101.1",
+ "retransmitCounter":0,
+ "requestCounter":0,
+ "dbSummaryCounter":0
+ }
+ ],
+ "192.168.0.12":[
+ {
+ "priority":0,
+ "state":"Full\/DROther",
+ "address":"192.168.101.12",
+ "ifaceName":"r1-eth1:192.168.101.1",
+ "retransmitCounter":0,
+ "requestCounter":0,
+ "dbSummaryCounter":0
+ }
+ ],
+ "192.168.0.13":[
+ {
+ "priority":0,
+ "state":"Full\/DROther",
+ "address":"192.168.101.13",
+ "ifaceName":"r1-eth1:192.168.101.1",
+ "retransmitCounter":0,
+ "requestCounter":0,
+ "dbSummaryCounter":0
+ }
+ ],
+ "192.168.0.14":[
+ {
+ "priority":0,
+ "state":"Full\/DROther",
+ "address":"192.168.101.14",
+ "ifaceName":"r1-eth1:192.168.101.1",
+ "retransmitCounter":0,
+ "requestCounter":0,
+ "dbSummaryCounter":0
+ }
+ ],
+ "192.168.0.15":[
+ {
+ "priority":0,
+ "state":"Full\/DROther",
+ "address":"192.168.101.15",
+ "ifaceName":"r1-eth1:192.168.101.1",
+ "retransmitCounter":0,
+ "requestCounter":0,
+ "dbSummaryCounter":0
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/pim_acl/r1/ospfd.conf b/tests/topotests/pim_acl/r1/ospfd.conf
new file mode 100644
index 0000000000..e1f47fb3b1
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/ospfd.conf
@@ -0,0 +1,16 @@
+hostname r1
+!
+debug ospf event
+!
+interface r1-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 20
+!
+router ospf
+ ospf router-id 192.168.0.1
+ passive-interface r1-eth0
+ network 192.168.0.1/32 area 0
+ network 192.168.100.0/24 area 0
+ network 192.168.101.0/24 area 0
+
diff --git a/tests/topotests/pim_acl/r1/pim_neighbor.json b/tests/topotests/pim_acl/r1/pim_neighbor.json
new file mode 100644
index 0000000000..ae95e8db14
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/pim_neighbor.json
@@ -0,0 +1,31 @@
+{
+ "r1-eth0":{
+ },
+ "r1-eth1":{
+ "192.168.101.12":{
+ "interface":"r1-eth1",
+ "neighbor":"192.168.101.12",
+ "drPriority":1
+ },
+ "192.168.101.15":{
+ "interface":"r1-eth1",
+ "neighbor":"192.168.101.15",
+ "drPriority":1
+ },
+ "192.168.101.14":{
+ "interface":"r1-eth1",
+ "neighbor":"192.168.101.14",
+ "drPriority":1
+ },
+ "192.168.101.11":{
+ "interface":"r1-eth1",
+ "neighbor":"192.168.101.11",
+ "drPriority":1
+ },
+ "192.168.101.13":{
+ "interface":"r1-eth1",
+ "neighbor":"192.168.101.13",
+ "drPriority":1
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r1/pimd.conf b/tests/topotests/pim_acl/r1/pimd.conf
new file mode 100644
index 0000000000..a148c73146
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/pimd.conf
@@ -0,0 +1,31 @@
+hostname r1
+!
+debug igmp events
+debug igmp packets
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+ip pim rp 192.168.0.11 prefix-list rp-pl-1
+ip pim rp 192.168.0.12 prefix-list rp-pl-2
+ip pim rp 192.168.0.13 prefix-list rp-pl-3
+ip pim rp 192.168.0.14 prefix-list rp-pl-4
+ip pim rp 192.168.0.15 prefix-list rp-pl-5
+ip pim join-prune-interval 5
+!
+interface r1-eth0
+ ip igmp
+ ip igmp version 2
+ ip pim
+!
+interface r1-eth1
+ ip pim
+!
+ip prefix-list rp-pl-1 seq 10 permit 239.100.0.0/28
+ip prefix-list rp-pl-2 seq 10 permit 239.100.0.17/32
+ip prefix-list rp-pl-3 seq 10 permit 239.100.0.32/27
+ip prefix-list rp-pl-4 seq 10 permit 239.100.0.128/25
+ip prefix-list rp-pl-4 seq 20 permit 239.100.0.96/28
+ip prefix-list rp-pl-5 seq 10 permit 239.100.0.64/28
diff --git a/tests/topotests/pim_acl/r1/zebra.conf b/tests/topotests/pim_acl/r1/zebra.conf
new file mode 100644
index 0000000000..74feb8f6a7
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/zebra.conf
@@ -0,0 +1,18 @@
+!
+hostname r1
+log file zebra.log
+!
+ip forwarding
+ipv6 forwarding
+!
+interface lo
+ ip address 192.168.0.1/32
+!
+interface r1-eth0
+ description connection to h1 via sw1
+ ip address 192.168.100.1/24
+!
+interface r1-eth1
+ description connection to r11/12/13/14/15 via sw2
+ ip address 192.168.101.1/24
+!
diff --git a/tests/topotests/pim_acl/r11/acl_1_pim_join.json b/tests/topotests/pim_acl/r11/acl_1_pim_join.json
new file mode 100644
index 0000000000..289bf51e76
--- /dev/null
+++ b/tests/topotests/pim_acl/r11/acl_1_pim_join.json
@@ -0,0 +1,19 @@
+{
+ "r11-eth0":{
+ "name":"r11-eth0",
+ "state":"up",
+ "address":"192.168.101.11",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.1":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.1",
+ "prune":"--:--",
+ "channelJoinName":"JOIN",
+ "protocolPim":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r11/ospfd.conf b/tests/topotests/pim_acl/r11/ospfd.conf
new file mode 100644
index 0000000000..e107220a4e
--- /dev/null
+++ b/tests/topotests/pim_acl/r11/ospfd.conf
@@ -0,0 +1,14 @@
+hostname r11
+!
+debug ospf event
+!
+interface r11-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 10
+!
+router ospf
+ ospf router-id 192.168.0.11
+ network 192.168.0.11/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_acl/r11/pimd.conf b/tests/topotests/pim_acl/r11/pimd.conf
new file mode 100644
index 0000000000..b1d45205da
--- /dev/null
+++ b/tests/topotests/pim_acl/r11/pimd.conf
@@ -0,0 +1,17 @@
+hostname r11
+!
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+ip pim rp 192.168.0.11 239.100.0.0/28
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r11-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_acl/r11/zebra.conf b/tests/topotests/pim_acl/r11/zebra.conf
new file mode 100644
index 0000000000..137706d245
--- /dev/null
+++ b/tests/topotests/pim_acl/r11/zebra.conf
@@ -0,0 +1,13 @@
+!
+hostname r11
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.11/32
+!
+interface r11-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.11/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_acl/r12/acl_2_pim_join.json b/tests/topotests/pim_acl/r12/acl_2_pim_join.json
new file mode 100644
index 0000000000..76ab7ee701
--- /dev/null
+++ b/tests/topotests/pim_acl/r12/acl_2_pim_join.json
@@ -0,0 +1,19 @@
+{
+ "r12-eth0":{
+ "name":"r12-eth0",
+ "state":"up",
+ "address":"192.168.101.12",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.17":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.17",
+ "prune":"--:--",
+ "channelJoinName":"JOIN",
+ "protocolPim":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r12/ospfd.conf b/tests/topotests/pim_acl/r12/ospfd.conf
new file mode 100644
index 0000000000..f9203c78e4
--- /dev/null
+++ b/tests/topotests/pim_acl/r12/ospfd.conf
@@ -0,0 +1,14 @@
+hostname r12
+!
+debug ospf event
+!
+interface r12-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 0
+!
+router ospf
+ ospf router-id 192.168.0.12
+ network 192.168.0.12/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_acl/r12/pimd.conf b/tests/topotests/pim_acl/r12/pimd.conf
new file mode 100644
index 0000000000..ba9e7d902f
--- /dev/null
+++ b/tests/topotests/pim_acl/r12/pimd.conf
@@ -0,0 +1,17 @@
+hostname r12
+!
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+ip pim rp 192.168.0.12 239.100.0.17/32
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r12-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_acl/r12/zebra.conf b/tests/topotests/pim_acl/r12/zebra.conf
new file mode 100644
index 0000000000..bede104906
--- /dev/null
+++ b/tests/topotests/pim_acl/r12/zebra.conf
@@ -0,0 +1,13 @@
+!
+hostname r12
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.12/32
+!
+interface r12-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.12/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_acl/r13/acl_3_pim_join.json b/tests/topotests/pim_acl/r13/acl_3_pim_join.json
new file mode 100644
index 0000000000..48ad72cbe1
--- /dev/null
+++ b/tests/topotests/pim_acl/r13/acl_3_pim_join.json
@@ -0,0 +1,19 @@
+{
+ "r13-eth0":{
+ "name":"r13-eth0",
+ "state":"up",
+ "address":"192.168.101.13",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.32":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.32",
+ "prune":"--:--",
+ "channelJoinName":"JOIN",
+ "protocolPim":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r13/ospfd.conf b/tests/topotests/pim_acl/r13/ospfd.conf
new file mode 100644
index 0000000000..830c5a14b6
--- /dev/null
+++ b/tests/topotests/pim_acl/r13/ospfd.conf
@@ -0,0 +1,14 @@
+hostname r13
+!
+debug ospf event
+!
+interface r13-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 0
+!
+router ospf
+ ospf router-id 192.168.0.13
+ network 192.168.0.13/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_acl/r13/pimd.conf b/tests/topotests/pim_acl/r13/pimd.conf
new file mode 100644
index 0000000000..2ff1743574
--- /dev/null
+++ b/tests/topotests/pim_acl/r13/pimd.conf
@@ -0,0 +1,17 @@
+hostname r13
+!
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+ip pim rp 192.168.0.13 239.100.0.32/27
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r13-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_acl/r13/zebra.conf b/tests/topotests/pim_acl/r13/zebra.conf
new file mode 100644
index 0000000000..f9ff27abac
--- /dev/null
+++ b/tests/topotests/pim_acl/r13/zebra.conf
@@ -0,0 +1,13 @@
+!
+hostname r13
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.13/32
+!
+interface r13-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.13/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_acl/r14/acl_4_pim_join.json b/tests/topotests/pim_acl/r14/acl_4_pim_join.json
new file mode 100644
index 0000000000..46d86dd40d
--- /dev/null
+++ b/tests/topotests/pim_acl/r14/acl_4_pim_join.json
@@ -0,0 +1,19 @@
+{
+ "r14-eth0":{
+ "name":"r14-eth0",
+ "state":"up",
+ "address":"192.168.101.14",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.255":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.255",
+ "prune":"--:--",
+ "channelJoinName":"JOIN",
+ "protocolPim":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r14/acl_5_pim_join.json b/tests/topotests/pim_acl/r14/acl_5_pim_join.json
new file mode 100644
index 0000000000..2b291a8a0c
--- /dev/null
+++ b/tests/topotests/pim_acl/r14/acl_5_pim_join.json
@@ -0,0 +1,19 @@
+{
+ "r14-eth0":{
+ "name":"r14-eth0",
+ "state":"up",
+ "address":"192.168.101.14",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.97":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.97",
+ "prune":"--:--",
+ "channelJoinName":"JOIN",
+ "protocolPim":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r14/ospfd.conf b/tests/topotests/pim_acl/r14/ospfd.conf
new file mode 100644
index 0000000000..422e4c08b0
--- /dev/null
+++ b/tests/topotests/pim_acl/r14/ospfd.conf
@@ -0,0 +1,14 @@
+hostname r14
+!
+debug ospf event
+!
+interface r14-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 0
+!
+router ospf
+ ospf router-id 192.168.0.14
+ network 192.168.0.14/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_acl/r14/pimd.conf b/tests/topotests/pim_acl/r14/pimd.conf
new file mode 100644
index 0000000000..1324a9e40b
--- /dev/null
+++ b/tests/topotests/pim_acl/r14/pimd.conf
@@ -0,0 +1,18 @@
+hostname r14
+!
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+ip pim rp 192.168.0.14 239.100.0.96/28
+ip pim rp 192.168.0.14 239.100.0.128/25
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r14-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_acl/r14/zebra.conf b/tests/topotests/pim_acl/r14/zebra.conf
new file mode 100644
index 0000000000..8761b46206
--- /dev/null
+++ b/tests/topotests/pim_acl/r14/zebra.conf
@@ -0,0 +1,13 @@
+!
+hostname r14
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.14/32
+!
+interface r14-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.14/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_acl/r15/acl_6_pim_join.json b/tests/topotests/pim_acl/r15/acl_6_pim_join.json
new file mode 100644
index 0000000000..05fed4ecc5
--- /dev/null
+++ b/tests/topotests/pim_acl/r15/acl_6_pim_join.json
@@ -0,0 +1,19 @@
+{
+ "r15-eth0":{
+ "name":"r15-eth0",
+ "state":"up",
+ "address":"192.168.101.15",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.70":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.70",
+ "prune":"--:--",
+ "channelJoinName":"JOIN",
+ "protocolPim":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r15/ospfd.conf b/tests/topotests/pim_acl/r15/ospfd.conf
new file mode 100644
index 0000000000..cd4d7b3875
--- /dev/null
+++ b/tests/topotests/pim_acl/r15/ospfd.conf
@@ -0,0 +1,14 @@
+hostname r15
+!
+debug ospf event
+!
+interface r15-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 0
+!
+router ospf
+ ospf router-id 192.168.0.15
+ network 192.168.0.15/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_acl/r15/pimd.conf b/tests/topotests/pim_acl/r15/pimd.conf
new file mode 100644
index 0000000000..f47e78c221
--- /dev/null
+++ b/tests/topotests/pim_acl/r15/pimd.conf
@@ -0,0 +1,17 @@
+hostname r15
+!
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+ip pim rp 192.168.0.15 239.100.0.64/28
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r15-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_acl/r15/zebra.conf b/tests/topotests/pim_acl/r15/zebra.conf
new file mode 100644
index 0000000000..f6909dd020
--- /dev/null
+++ b/tests/topotests/pim_acl/r15/zebra.conf
@@ -0,0 +1,13 @@
+!
+hostname r15
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.15/32
+!
+interface r15-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.15/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_acl/test_pim_acl.py b/tests/topotests/pim_acl/test_pim_acl.py
new file mode 100755
index 0000000000..848f7fa8ed
--- /dev/null
+++ b/tests/topotests/pim_acl/test_pim_acl.py
@@ -0,0 +1,418 @@
+#!/usr/bin/env python
+
+#
+# test_pim_acl.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_pim_acl.py: Test PIM with RP selection using ACLs
+"""
+
+# Test PIM RP selection with ACLs
+#
+# Testing RP selection with ACLs. R1 uses multiple ACLs
+# to select desired RPs (R11 to R15)
+#
+# Test steps:
+# - setup_module()
+# Create topology. Hosts are only using zebra/staticd,
+# no PIM, no OSPF (using IGMPv2 for multicast)
+# - test_ospf_convergence()
+# Wait for OSPF convergence in each VRF. OSPF is run on
+# R1 and R11 - R15.
+# - test_pim_convergence()
+# Wait for PIM convergence on all routers. PIM is run on
+# R1 and R11 - R15.
+# - test_mcast_acl_1():
+# Test 1st ACL entry 239.100.0.0/28 with 239.100.0.1 which
+# should use R11 as RP
+# Stop multicast after verification
+# - test_mcast_acl_2():
+# Test 2nd ACL entry 239.100.0.17/32 with 239.100.0.17 which
+# should use R12 as RP
+# Stop multicast after verification
+# - test_mcast_acl_3():
+# Test 3rd ACL entry 239.100.0.32/27 with 239.100.0.32 which
+# should use R13 as RP
+# Stop multicast after verification
+# - test_mcast_acl_4():
+# Test 4th ACL entry 239.100.0.128/25 with 239.100.0.255 which
+# should use R14 as RP
+# Stop multicast after verification
+# - test_mcast_acl_5():
+# Test 5th ACL entry 239.100.0.96/28 with 239.100.0.97 which
+# should use R14 as RP
+# Stop multicast after verification
+# - test_mcast_acl_6():
+# Test 6th ACL entry 239.100.0.64/28 with 239.100.0.70 which
+# should use R15 as RP
+# Stop multicast after verification
+# - teardown_module()
+# shutdown topology
+#
+
+
+TOPOLOGY = """
+ +----------+
+ | Host H2 |
+ | Source |
+ +----------+
+ .2 |
+ +-----------+ | +----------+
+ | | .1 | .11 | Host R11 |
++---------+ | R1 |---------+--------| PIM RP |
+| Host H1 | 192.168.100.0/24 | | 192.168.101.0/24 +----------+
+| receive |------------------| uses ACLs | | +----------+
+|IGMP JOIN| .10 .1 | to pick | | .12 | Host R12 |
++---------+ | RP | +--------| PIM RP |
+ | | | +----------+
+ +-----------+ | +----------+
+ | .13 | Host R13 |
+ +--------| PIM RP |
+ | +----------+
+ | +----------+
+ | .14 | Host R14 |
+ +--------| PIM RP |
+ | +----------+
+ | +----------+
+ | .15 | Host R15 |
+ +--------| PIM RP |
+ +----------+
+"""
+
+import json
+import functools
+import os
+import sys
+import pytest
+import re
+import time
+from time import sleep
+import socket
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+pytestmark = [pytest.mark.pimd]
+
+
+#
+# Test global variables:
+# They are used to handle communicating with external application.
+#
+APP_SOCK_PATH = '/tmp/topotests/apps.sock'
+HELPER_APP_PATH = os.path.join(CWD, "../lib/mcast-tester.py")
+app_listener = None
+app_clients = {}
+
+def listen_to_applications():
+ "Start listening socket to connect with applications."
+ # Remove old socket.
+ try:
+ os.unlink(APP_SOCK_PATH)
+ except OSError:
+ pass
+
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0)
+ sock.bind(APP_SOCK_PATH)
+ sock.listen(10)
+ global app_listener
+ app_listener = sock
+
+def accept_host(host):
+ "Accept connection from application running in hosts."
+ global app_listener, app_clients
+ conn = app_listener.accept()
+ app_clients[host] = {
+ 'fd': conn[0],
+ 'address': conn[1]
+ }
+
+def close_applications():
+ "Signal applications to stop and close all sockets."
+ global app_listener, app_clients
+
+ if app_listener:
+ # Close listening socket.
+ app_listener.close()
+
+ # Remove old socket.
+ try:
+ os.unlink(APP_SOCK_PATH)
+ except OSError:
+ pass
+
+ # Close all host connections.
+ for host in ["h1", "h2"]:
+ if app_clients.get(host) is None:
+ continue
+ app_clients[host]["fd"].close()
+
+ # Reset listener and clients data struct
+ app_listener = None
+ app_clients = {}
+
+
+class PIMACLTopo(Topo):
+ "PIM ACL Test Topology"
+
+ def build(self):
+ tgen = get_topogen(self)
+
+ # Create the hosts
+ for hostNum in range(1,3):
+ tgen.add_router("h{}".format(hostNum))
+
+ # Create the main router
+ tgen.add_router("r1")
+
+ # Create the PIM RP routers
+ for rtrNum in range(11, 16):
+ tgen.add_router("r{}".format(rtrNum))
+
+ # Setup Switches and connections
+ for swNum in range(1, 3):
+ tgen.add_switch("sw{}".format(swNum))
+
+ # Add connections H1 to R1 switch sw1
+ tgen.gears["h1"].add_link(tgen.gears["sw1"])
+ tgen.gears["r1"].add_link(tgen.gears["sw1"])
+
+ # Add connections R1 to R1x switch sw2
+ tgen.gears["r1"].add_link(tgen.gears["sw2"])
+ tgen.gears["h2"].add_link(tgen.gears["sw2"])
+ tgen.gears["r11"].add_link(tgen.gears["sw2"])
+ tgen.gears["r12"].add_link(tgen.gears["sw2"])
+ tgen.gears["r13"].add_link(tgen.gears["sw2"])
+ tgen.gears["r14"].add_link(tgen.gears["sw2"])
+ tgen.gears["r15"].add_link(tgen.gears["sw2"])
+
+
+#####################################################
+#
+# Tests starting
+#
+#####################################################
+
+def setup_module(module):
+ logger.info("PIM RP ACL Topology: \n {}".format(TOPOLOGY))
+
+ tgen = Topogen(PIMACLTopo, module.__name__)
+ tgen.start_topology()
+
+ # Starting Routers
+ router_list = tgen.routers()
+
+ for rname, router in router_list.items():
+ logger.info("Loading router %s" % rname)
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ if rname[0] != 'h':
+ # Only load ospf on routers, not on end hosts
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_PIM, os.path.join(CWD, "{}/pimd.conf".format(rname))
+ )
+ tgen.start_router()
+
+
+def teardown_module(module):
+ tgen = get_topogen()
+ tgen.stop_topology()
+ close_applications()
+
+
+def test_ospf_convergence():
+ "Test for OSPFv2 convergence"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Checking OSPFv2 convergence on router r1")
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/ospf_neighbor.json")
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip ospf neighbor json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "OSPF router R1 did not converge"
+ assert res is None, assertmsg
+
+
+def test_pim_convergence():
+ "Test for PIM convergence"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Checking PIM convergence on router r1")
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/pim_neighbor.json")
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip pim neighbor json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "PIM router R1 did not converge"
+ assert res is None, assertmsg
+
+
+
+def check_mcast_entry(entry, mcastaddr, pimrp):
+ "Helper function to check RP"
+ tgen = get_topogen()
+
+ logger.info("Testing PIM RP selection for ACL {} entry using {}".format(entry, mcastaddr));
+
+ # Start applications socket.
+ listen_to_applications()
+
+ tgen.gears["h2"].run("{} --send='0.7' '{}' '{}' '{}' &".format(
+ HELPER_APP_PATH, APP_SOCK_PATH, mcastaddr, 'h2-eth0'))
+ accept_host("h2")
+
+ tgen.gears["h1"].run("{} '{}' '{}' '{}' &".format(
+ HELPER_APP_PATH, APP_SOCK_PATH, mcastaddr, 'h1-eth0'))
+ accept_host("h1")
+
+ logger.info("mcast join and source for {} started".format(mcastaddr))
+
+ # tgen.mininet_cli()
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/acl_{}_pim_join.json".format(entry))
+ expected = json.loads(open(reffile).read())
+
+ logger.info("verifying pim join on r1 for {}".format(mcastaddr))
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip pim join json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "PIM router r1 did not show join status"
+ assert res is None, assertmsg
+
+ logger.info("verifying pim join on PIM RP {} for {}".format(pimrp, mcastaddr))
+ router = tgen.gears[pimrp]
+ reffile = os.path.join(CWD, "{}/acl_{}_pim_join.json".format(pimrp, entry))
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip pim join json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "PIM router {} did not get selected as the PIM RP".format(pimrp)
+ assert res is None, assertmsg
+
+ close_applications()
+ return
+
+
+def test_mcast_acl_1():
+ "Test 1st ACL entry 239.100.0.0/28 with 239.100.0.1"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_mcast_entry(1, '239.100.0.1', 'r11')
+
+
+def test_mcast_acl_2():
+ "Test 2nd ACL entry 239.100.0.17/32 with 239.100.0.17"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_mcast_entry(2, '239.100.0.17', 'r12')
+
+
+def test_mcast_acl_3():
+ "Test 3rd ACL entry 239.100.0.32/27 with 239.100.0.32"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_mcast_entry(3, '239.100.0.32', 'r13')
+
+
+def test_mcast_acl_4():
+ "Test 4th ACL entry 239.100.0.128/25 with 239.100.0.255"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_mcast_entry(4, '239.100.0.255', 'r14')
+
+
+def test_mcast_acl_5():
+ "Test 5th ACL entry 239.100.0.96/28 with 239.100.0.97"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_mcast_entry(5, '239.100.0.97', 'r14')
+
+
+def test_mcast_acl_6():
+ "Test 6th ACL entry 239.100.0.64/28 with 239.100.0.70"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_mcast_entry(6, '239.100.0.70', 'r15')
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/pim_basic/r1/pimd.conf b/tests/topotests/pim_basic/r1/pimd.conf
index f64a46deb3..737019fa51 100644
--- a/tests/topotests/pim_basic/r1/pimd.conf
+++ b/tests/topotests/pim_basic/r1/pimd.conf
@@ -15,3 +15,4 @@ interface lo
ip pim
!
ip pim rp 10.254.0.3
+ip pim join-prune-interval 5
diff --git a/tests/topotests/pim_basic/rp/pimd.conf b/tests/topotests/pim_basic/rp/pimd.conf
index 6e35c97971..fd26bc4d71 100644
--- a/tests/topotests/pim_basic/rp/pimd.conf
+++ b/tests/topotests/pim_basic/rp/pimd.conf
@@ -6,6 +6,7 @@ interface rp-eth0
interface lo
ip pim
!
+ip pim join-prune-interval 5
ip pim rp 10.254.0.3
ip pim register-accept-list ACCEPT
diff --git a/tests/topotests/pim_basic_topo2/r2/pimd.conf b/tests/topotests/pim_basic_topo2/r2/pimd.conf
index 0b32ded19a..9f389deb11 100644
--- a/tests/topotests/pim_basic_topo2/r2/pimd.conf
+++ b/tests/topotests/pim_basic_topo2/r2/pimd.conf
@@ -10,3 +10,4 @@ interface r2-eth2
ip pim
ip pim bfd
!
+ip pim join-prune-interval 5
diff --git a/tests/topotests/pim_igmp_vrf/h1/zebra.conf b/tests/topotests/pim_igmp_vrf/h1/zebra.conf
new file mode 100644
index 0000000000..3d6540d40c
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/h1/zebra.conf
@@ -0,0 +1,10 @@
+!
+hostname h1
+log file zebra.log
+!
+interface h1-eth0
+ description connection to r1 via sw1
+ ip address 192.168.100.10/24
+!
+ip route 0.0.0.0/0 192.168.100.1
+!
diff --git a/tests/topotests/pim_igmp_vrf/h2/zebra.conf b/tests/topotests/pim_igmp_vrf/h2/zebra.conf
new file mode 100644
index 0000000000..95342f9e8a
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/h2/zebra.conf
@@ -0,0 +1,8 @@
+hostname h2
+!
+interface h2-eth0
+ description connection to r1 via sw2
+ ip address 192.168.101.2/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_igmp_vrf/h3/zebra.conf b/tests/topotests/pim_igmp_vrf/h3/zebra.conf
new file mode 100644
index 0000000000..ef99b1cd8f
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/h3/zebra.conf
@@ -0,0 +1,10 @@
+!
+hostname h3
+log file zebra.log
+!
+interface h3-eth0
+ description connection to r1 via sw3
+ ip address 192.168.100.20/24
+!
+ip route 0.0.0.0/0 192.168.100.1
+!
diff --git a/tests/topotests/pim_igmp_vrf/h4/zebra.conf b/tests/topotests/pim_igmp_vrf/h4/zebra.conf
new file mode 100644
index 0000000000..6a2e466000
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/h4/zebra.conf
@@ -0,0 +1,8 @@
+hostname h4
+!
+interface h4-eth0
+ description connection to r1 via sw4
+ ip address 192.168.101.4/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json b/tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json
new file mode 100644
index 0000000000..604d25fac1
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json
@@ -0,0 +1,15 @@
+{
+ "blue":{
+ "vrfName":"blue",
+ "neighbors":{
+ "192.168.0.11":[
+ {
+ "priority":10,
+ "state":"Full\/Backup",
+ "address":"192.168.101.11",
+ "ifaceName":"r1-eth1:192.168.101.1"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json b/tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json
new file mode 100644
index 0000000000..456bb87520
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json
@@ -0,0 +1,16 @@
+{
+ "red":{
+ "vrfName":"red",
+ "neighbors":{
+ "192.168.0.12":[
+ {
+ "priority":10,
+ "state":"Full\/Backup",
+ "address":"192.168.101.12",
+ "ifaceName":"r1-eth3:192.168.101.1"
+ }
+ ]
+ }
+ }
+}
+
diff --git a/tests/topotests/pim_igmp_vrf/r1/ospfd.conf b/tests/topotests/pim_igmp_vrf/r1/ospfd.conf
new file mode 100644
index 0000000000..263b5867cc
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/ospfd.conf
@@ -0,0 +1,26 @@
+hostname r1
+!
+debug ospf event
+!
+!
+interface r1-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 20
+!
+interface r1-eth3
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 20
+!
+router ospf vrf blue
+ ospf router-id 192.168.0.1
+ network 192.168.0.1/32 area 0
+ network 192.168.100.0/24 area 0
+ network 192.168.101.0/24 area 0
+router ospf vrf red
+ ospf router-id 192.168.0.1
+ network 192.168.0.1/32 area 0
+ network 192.168.100.0/24 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_igmp_vrf/r1/pim_blue_join.json b/tests/topotests/pim_igmp_vrf/r1/pim_blue_join.json
new file mode 100644
index 0000000000..8568bae2bc
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/pim_blue_join.json
@@ -0,0 +1,22 @@
+{
+ "r1-eth0":{
+ "name":"r1-eth0",
+ "state":"up",
+ "address":"192.168.100.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.1":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.1",
+ "upTime":"--:--:--",
+ "expire":"--:--",
+ "prune":"--:--",
+ "channelJoinName":"NOINFO",
+ "protocolIgmp":1
+ }
+ }
+ }
+}
+
diff --git a/tests/topotests/pim_igmp_vrf/r1/pim_blue_neighbor.json b/tests/topotests/pim_igmp_vrf/r1/pim_blue_neighbor.json
new file mode 100644
index 0000000000..ea7d4aca6f
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/pim_blue_neighbor.json
@@ -0,0 +1,13 @@
+{
+ "blue":{
+ },
+ "r1-eth0":{
+ },
+ "r1-eth1":{
+ "192.168.101.11":{
+ "interface":"r1-eth1",
+ "neighbor":"192.168.101.11",
+ "drPriority":1
+ }
+ }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r1/pim_blue_pimreg11.json b/tests/topotests/pim_igmp_vrf/r1/pim_blue_pimreg11.json
new file mode 100644
index 0000000000..d3642f854a
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/pim_blue_pimreg11.json
@@ -0,0 +1,14 @@
+{
+ "pimreg11":{
+ "name":"pimreg11",
+ "state":"up",
+ "address":"0.0.0.0",
+ "flagAllMulticast":true,
+ "lanDelayEnabled":true,
+ "drAddress":"*",
+ "drPriority":1,
+ "drUptime":"--:--:--",
+ "drElections":0,
+ "drChanges":0
+ }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r1/pim_red_join.json b/tests/topotests/pim_igmp_vrf/r1/pim_red_join.json
new file mode 100644
index 0000000000..d0037ca4b0
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/pim_red_join.json
@@ -0,0 +1,21 @@
+{
+ "r1-eth2":{
+ "name":"r1-eth2",
+ "state":"up",
+ "address":"192.168.100.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.1":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.1",
+ "upTime":"--:--:--",
+ "expire":"--:--",
+ "prune":"--:--",
+ "channelJoinName":"NOINFO",
+ "protocolIgmp":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r1/pim_red_neighbor.json b/tests/topotests/pim_igmp_vrf/r1/pim_red_neighbor.json
new file mode 100644
index 0000000000..e17b40854a
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/pim_red_neighbor.json
@@ -0,0 +1,13 @@
+{
+ "r1-eth2":{
+ },
+ "r1-eth3":{
+ "192.168.101.12":{
+ "interface":"r1-eth3",
+ "neighbor":"192.168.101.12",
+ "drPriority":1
+ }
+ },
+ "red":{
+ }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r1/pim_red_pimreg12.json b/tests/topotests/pim_igmp_vrf/r1/pim_red_pimreg12.json
new file mode 100644
index 0000000000..45b6cd9645
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/pim_red_pimreg12.json
@@ -0,0 +1,14 @@
+{
+ "pimreg12":{
+ "name":"pimreg12",
+ "state":"up",
+ "address":"0.0.0.0",
+ "flagAllMulticast":true,
+ "lanDelayEnabled":true,
+ "drAddress":"*",
+ "drPriority":1,
+ "drUptime":"--:--:--",
+ "drElections":0,
+ "drChanges":0
+ }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r1/pimd.conf b/tests/topotests/pim_igmp_vrf/r1/pimd.conf
new file mode 100644
index 0000000000..f04c255de9
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/pimd.conf
@@ -0,0 +1,27 @@
+hostname r1
+!
+debug igmp events
+debug igmp packets
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+interface r1-eth0
+ ip igmp
+ ip igmp version 2
+ ip pim
+!
+interface r1-eth1
+ ip pim
+!
+interface r1-eth2
+ ip igmp
+ ip igmp version 2
+ ip pim
+!
+interface r1-eth3
+ ip pim
+!
+ip pim join-prune-interval 5
diff --git a/tests/topotests/pim_igmp_vrf/r1/zebra.conf b/tests/topotests/pim_igmp_vrf/r1/zebra.conf
new file mode 100644
index 0000000000..9da9280945
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/zebra.conf
@@ -0,0 +1,30 @@
+!
+hostname r1
+log file zebra.log
+!
+ip forwarding
+ipv6 forwarding
+!
+interface blue vrf blue
+ ip address 192.168.0.1/32
+!
+interface red vrf red
+ ip address 192.168.0.1/32
+!
+interface r1-eth0 vrf blue
+ description connection to h1 via sw1
+ ip address 192.168.100.1/24
+!
+interface r1-eth1 vrf blue
+ description connection to r11 via sw2
+ ip address 192.168.101.1/24
+!
+interface r1-eth2 vrf red
+ description connection to h1 via sw3
+ ip address 192.168.100.1/24
+!
+interface r1-eth3 vrf red
+ description connection to r12 via sw4
+ ip address 192.168.101.1/24
+!
+
diff --git a/tests/topotests/pim_igmp_vrf/r11/ospfd.conf b/tests/topotests/pim_igmp_vrf/r11/ospfd.conf
new file mode 100644
index 0000000000..e107220a4e
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r11/ospfd.conf
@@ -0,0 +1,14 @@
+hostname r11
+!
+debug ospf event
+!
+interface r11-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 10
+!
+router ospf
+ ospf router-id 192.168.0.11
+ network 192.168.0.11/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_igmp_vrf/r11/pim_blue_join.json b/tests/topotests/pim_igmp_vrf/r11/pim_blue_join.json
new file mode 100644
index 0000000000..289bf51e76
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r11/pim_blue_join.json
@@ -0,0 +1,19 @@
+{
+ "r11-eth0":{
+ "name":"r11-eth0",
+ "state":"up",
+ "address":"192.168.101.11",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.1":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.1",
+ "prune":"--:--",
+ "channelJoinName":"JOIN",
+ "protocolPim":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r11/pimd.conf b/tests/topotests/pim_igmp_vrf/r11/pimd.conf
new file mode 100644
index 0000000000..b1d45205da
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r11/pimd.conf
@@ -0,0 +1,17 @@
+hostname r11
+!
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+ip pim rp 192.168.0.11 239.100.0.0/28
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r11-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_igmp_vrf/r11/zebra.conf b/tests/topotests/pim_igmp_vrf/r11/zebra.conf
new file mode 100644
index 0000000000..137706d245
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r11/zebra.conf
@@ -0,0 +1,13 @@
+!
+hostname r11
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.11/32
+!
+interface r11-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.11/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_igmp_vrf/r12/ospfd.conf b/tests/topotests/pim_igmp_vrf/r12/ospfd.conf
new file mode 100644
index 0000000000..03acc82c1d
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r12/ospfd.conf
@@ -0,0 +1,14 @@
+hostname r12
+!
+debug ospf event
+!
+interface r12-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 10
+!
+router ospf
+ ospf router-id 192.168.0.12
+ network 192.168.0.12/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_igmp_vrf/r12/pim_red_join.json b/tests/topotests/pim_igmp_vrf/r12/pim_red_join.json
new file mode 100644
index 0000000000..6926246568
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r12/pim_red_join.json
@@ -0,0 +1,19 @@
+{
+ "r12-eth0":{
+ "name":"r12-eth0",
+ "state":"up",
+ "address":"192.168.101.12",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.1":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.1",
+ "prune":"--:--",
+ "channelJoinName":"JOIN",
+ "protocolPim":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r12/pimd.conf b/tests/topotests/pim_igmp_vrf/r12/pimd.conf
new file mode 100644
index 0000000000..5cb76efa22
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r12/pimd.conf
@@ -0,0 +1,17 @@
+hostname r12
+!
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+ip pim rp 192.168.0.12 239.100.0.0/28
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r12-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_igmp_vrf/r12/zebra.conf b/tests/topotests/pim_igmp_vrf/r12/zebra.conf
new file mode 100644
index 0000000000..bede104906
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r12/zebra.conf
@@ -0,0 +1,13 @@
+!
+hostname r12
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.12/32
+!
+interface r12-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.12/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_igmp_vrf/test_pim_vrf.py b/tests/topotests/pim_igmp_vrf/test_pim_vrf.py
new file mode 100755
index 0000000000..298adef9c6
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/test_pim_vrf.py
@@ -0,0 +1,462 @@
+#!/usr/bin/env python
+
+#
+# test_pim_vrf.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_pim_vrf.py: Test PIM with VRFs.
+"""
+
+# Tests PIM with VRF
+#
+# R1 is split into 2 VRF: Blue and Red, the others are normal
+# routers and Hosts
+# There are 2 similar topologies with overlapping IPs in each
+# section.
+#
+# Test steps:
+# - setup_module()
+# Create topology. Hosts are only using zebra/staticd,
+# no PIM, no OSPF (using IGMPv2 for multicast)
+# - test_ospf_convergence()
+# Wait for OSPF convergence in each VRF. OSPF is run on
+# R1, R11 and R12.
+# - test_pim_convergence()
+# Wait for PIM convergence in each VRF. PIM is run on
+# R1, R11 and R12. R11 is the RP for vrf blue, R12 is RP
+# for vrf red.
+# - test_vrf_pimreg_interfaces()
+# Adding PIM RP in VRF information and verify pimreg
+# interfaces in VRF blue and red
+# - test_mcast_vrf_blue()
+# Start multicast stream for group 239.100.0.1 from Host
+# H2 and join from Host H1 on vrf blue
+# Verify PIM JOIN status on R1 and R11
+# Stop multicast after verification
+# - test_mcast_vrf_red()
+# Start multicast stream for group 239.100.0.1 from Host
+# H4 and join from Host H3 on vrf blue
+# Verify PIM JOIN status on R1 and R12
+# Stop multicast after verification
+# - teardown_module(module)
+# shutdown topology
+#
+
+TOPOLOGY = """
+ +----------+
+ | Host H2 |
+ | Source |
+ +----------+
+ .2 |
++---------+ +------------+ | +---------+
+| Host H1 | 192.168.100.0/24 | | .1 | .11 | Host H2 |
+| receive |------------------| VRF Blue |---------+--------| PIM RP |
+|IGMP JOIN| .10 .1 | | 192.168.101.0/24 | |
++---------+ | | +---------+
+ =| = = R1 = = |=
++---------+ | | +---------+
+| Host H3 | 192.168.100.0/24 | | 192.168.101.0/24 | Host H4 |
+| receive |------------------| VRF Red |---------+--------| PIM RP |
+|IGMP JOIN| .20 .1 | | .1 | .12 | |
++---------+ +------------+ | +---------+
+ .4 |
+ +----------+
+ | Host H4 |
+ | Source |
+ +----------+
+"""
+
+import json
+import functools
+import os
+import sys
+import pytest
+import re
+import time
+from time import sleep
+import socket
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from lib.topotest import iproute2_is_vrf_capable
+from lib.common_config import (
+ required_linux_kernel_version)
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+pytestmark = [pytest.mark.pimd]
+
+
+#
+# Test global variables:
+# They are used to handle communicating with external application.
+#
+APP_SOCK_PATH = '/tmp/topotests/apps.sock'
+HELPER_APP_PATH = os.path.join(CWD, "../lib/mcast-tester.py")
+app_listener = None
+app_clients = {}
+
+def listen_to_applications():
+ "Start listening socket to connect with applications."
+ # Remove old socket.
+ try:
+ os.unlink(APP_SOCK_PATH)
+ except OSError:
+ pass
+
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0)
+ sock.bind(APP_SOCK_PATH)
+ sock.listen(10)
+ global app_listener
+ app_listener = sock
+
+def accept_host(host):
+ "Accept connection from application running in hosts."
+ global app_listener, app_clients
+ conn = app_listener.accept()
+ app_clients[host] = {
+ 'fd': conn[0],
+ 'address': conn[1]
+ }
+
+def close_applications():
+ "Signal applications to stop and close all sockets."
+ global app_listener, app_clients
+
+ if app_listener:
+ # Close listening socket.
+ app_listener.close()
+
+ # Remove old socket.
+ try:
+ os.unlink(APP_SOCK_PATH)
+ except OSError:
+ pass
+
+ # Close all host connections.
+ for host in ["h1", "h2"]:
+ if app_clients.get(host) is None:
+ continue
+ app_clients[host]["fd"].close()
+
+ # Reset listener and clients data struct
+ app_listener = None
+ app_clients = {}
+
+
+class PIMVRFTopo(Topo):
+ "PIM VRF Test Topology"
+
+ def build(self):
+ tgen = get_topogen(self)
+
+ # Create the hosts
+ for hostNum in range(1,5):
+ tgen.add_router("h{}".format(hostNum))
+
+ # Create the main router
+ tgen.add_router("r1")
+
+ # Create the PIM RP routers
+ for rtrNum in range(11, 13):
+ tgen.add_router("r{}".format(rtrNum))
+
+ # Setup Switches and connections
+ for swNum in range(1, 5):
+ tgen.add_switch("sw{}".format(swNum))
+
+ ################
+ # 1st set of connections to routers for VRF red
+ ################
+
+ # Add connections H1 to R1 switch sw1
+ tgen.gears["h1"].add_link(tgen.gears["sw1"])
+ tgen.gears["r1"].add_link(tgen.gears["sw1"])
+
+ # Add connections R1 to R1x switch sw2
+ tgen.gears["r1"].add_link(tgen.gears["sw2"])
+ tgen.gears["h2"].add_link(tgen.gears["sw2"])
+ tgen.gears["r11"].add_link(tgen.gears["sw2"])
+
+ ################
+ # 2nd set of connections to routers for vrf blue
+ ################
+
+ # Add connections H1 to R1 switch sw1
+ tgen.gears["h3"].add_link(tgen.gears["sw3"])
+ tgen.gears["r1"].add_link(tgen.gears["sw3"])
+
+ # Add connections R1 to R1x switch sw2
+ tgen.gears["r1"].add_link(tgen.gears["sw4"])
+ tgen.gears["h4"].add_link(tgen.gears["sw4"])
+ tgen.gears["r12"].add_link(tgen.gears["sw4"])
+
+#####################################################
+#
+# Tests starting
+#
+#####################################################
+
+def setup_module(module):
+ logger.info("PIM IGMP VRF Topology: \n {}".format(TOPOLOGY))
+
+ tgen = Topogen(PIMVRFTopo, module.__name__)
+ tgen.start_topology()
+
+ vrf_setup_cmds = [
+ "ip link add name blue type vrf table 11",
+ "ip link add name red type vrf table 12",
+ "ip link set dev blue up",
+ "ip link set dev red up",
+ "ip link set dev r1-eth0 vrf blue up",
+ "ip link set dev r1-eth1 vrf blue up",
+ "ip link set dev r1-eth2 vrf red up",
+ "ip link set dev r1-eth3 vrf red up",
+ ]
+
+ # Starting Routers
+ router_list = tgen.routers()
+
+ # Create VRF on r2 first and add it's interfaces
+ for cmd in vrf_setup_cmds:
+ tgen.net["r1"].cmd(cmd)
+
+ for rname, router in router_list.items():
+ logger.info("Loading router %s" % rname)
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ if rname[0] != 'h':
+ # Only load ospf on routers, not on end hosts
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_PIM, os.path.join(CWD, "{}/pimd.conf".format(rname))
+ )
+ tgen.start_router()
+
+
+def teardown_module(module):
+ tgen = get_topogen()
+ tgen.stop_topology()
+ close_applications()
+
+
+def test_ospf_convergence():
+ "Test for OSPFv2 convergence"
+ tgen = get_topogen()
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ # iproute2 needs to support VRFs for this suite to run.
+ if not iproute2_is_vrf_capable():
+ pytest.skip("Installed iproute2 version does not support VRFs")
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Checking OSPFv2 convergence on router r1 for VRF blue")
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/ospf_blue_neighbor.json")
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip ospf vrf blue neighbor json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "OSPF router R1 did not converge on VRF blue"
+ assert res is None, assertmsg
+
+ logger.info("Checking OSPFv2 convergence on router r1 for VRF red")
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/ospf_red_neighbor.json")
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip ospf vrf red neighbor json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "OSPF router R1 did not converge on VRF red"
+ assert res is None, assertmsg
+
+
+def test_pim_convergence():
+ "Test for PIM convergence"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Checking PIM convergence on router r1 for VRF red")
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/pim_red_neighbor.json")
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip pim vrf red neighbor json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=30, wait=2)
+ assertmsg = "PIM router R1 did not converge for VRF red"
+ assert res is None, assertmsg
+
+ logger.info("Checking PIM convergence on router r1 for VRF blue")
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/pim_blue_neighbor.json")
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip pim vrf blue neighbor json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=30, wait=2)
+ assertmsg = "PIM router R1 did not converge for VRF blue"
+ assert res is None, assertmsg
+
+
+def test_vrf_pimreg_interfaces():
+ "Adding PIM RP in VRF information and verify pimreg interfaces"
+ tgen = get_topogen()
+
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd("conf\ninterface blue\nip pim")
+ r1.vtysh_cmd("conf\nvrf blue\nip pim rp 192.168.0.11 239.100.0.1/32\nexit-vrf")
+
+ # Check pimreg11 interface on R1, VRF blue
+ reffile = os.path.join(CWD, "r1/pim_blue_pimreg11.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp, r1, "show ip pim vrf blue inter pimreg11 json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=5, wait=2)
+ assertmsg = "PIM router R1, VRF blue (table 11) pimreg11 interface missing or incorrect status"
+ assert res is None, assertmsg
+
+ r1.vtysh_cmd("conf\ninterface red\nip pim")
+ r1.vtysh_cmd("conf\nvrf red\nip pim rp 192.168.0.12 239.100.0.1/32\nexit-vrf")
+
+ # Check pimreg12 interface on R1, VRF red
+ reffile = os.path.join(CWD, "r1/pim_red_pimreg12.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp, r1, "show ip pim vrf red inter pimreg12 json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=5, wait=2)
+ assertmsg = "PIM router R1, VRF red (table 12) pimreg12 interface missing or incorrect status"
+ assert res is None, assertmsg
+
+
+##################################
+### Test PIM / IGMP with VRF
+##################################
+
+def check_mcast_entry(mcastaddr, pimrp, receiver, sender, vrf):
+ "Helper function to check RP"
+ tgen = get_topogen()
+
+ logger.info("Testing PIM for VRF {} entry using {}".format(vrf, mcastaddr));
+
+ # Start applications socket.
+ listen_to_applications()
+
+ tgen.gears[sender].run("{} --send='0.7' '{}' '{}' '{}' &".format(
+ HELPER_APP_PATH, APP_SOCK_PATH, mcastaddr, '{}-eth0'.format(sender)))
+ accept_host(sender)
+
+ tgen.gears[receiver].run("{} '{}' '{}' '{}' &".format(
+ HELPER_APP_PATH, APP_SOCK_PATH, mcastaddr, '{}-eth0'.format(receiver)))
+ accept_host(receiver)
+
+ logger.info("mcast join and source for {} started".format(mcastaddr))
+
+ # tgen.mininet_cli()
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/pim_{}_join.json".format(vrf))
+ expected = json.loads(open(reffile).read())
+
+ logger.info("verifying pim join on r1 for {} on VRF {}".format(mcastaddr, vrf))
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip pim vrf {} join json".format(vrf),
+ expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=10, wait=2)
+ assertmsg = "PIM router r1 did not show join status on VRF".format(vrf)
+ assert res is None, assertmsg
+
+ logger.info("verifying pim join on PIM RP {} for {}".format(pimrp, mcastaddr))
+ router = tgen.gears[pimrp]
+ reffile = os.path.join(CWD, "{}/pim_{}_join.json".format(pimrp, vrf))
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip pim join json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=10, wait=2)
+ assertmsg = "PIM router {} did not get selected as the PIM RP for VRF {}".format(pimrp, vrf)
+ assert res is None, assertmsg
+
+ close_applications()
+ return
+
+
+def test_mcast_vrf_blue():
+ "Test vrf blue with 239.100.0.1"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_mcast_entry('239.100.0.1', 'r11', 'h1', 'h2', 'blue')
+
+
+def test_mcast_vrf_red():
+ "Test vrf red with 239.100.0.1"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_mcast_entry('239.100.0.1', 'r12', 'h3', 'h4', 'red')
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))