summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/bgpd/test_peer_attr.c6
-rw-r--r--tests/lib/test_privs.c4
-rw-r--r--tests/topotests/Dockerfile8
-rw-r--r--tests/topotests/all-protocol-startup/r1/ipv4_routes.ref2
-rw-r--r--tests/topotests/all-protocol-startup/r1/zebra.conf3
-rwxr-xr-xtests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py2
-rwxr-xr-xtests/topotests/bgp-ecmp-topo2/ebgp_ecmp_topo2.json664
-rwxr-xr-xtests/topotests/bgp-ecmp-topo2/ibgp_ecmp_topo2.json674
-rwxr-xr-xtests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py817
-rwxr-xr-xtests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py813
-rwxr-xr-xtests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py6
-rwxr-xr-xtests/topotests/bgp-prefix-list-topo1/test_prefix_lists.py34
-rw-r--r--tests/topotests/bgp_comm-list_delete/test_bgp_comm-list_delete.py2
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/adjacencies.py14
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py8
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/scripts/adjacencies.py8
-rwxr-xr-xtests/topotests/docker/inner/compile_frr.sh1
-rwxr-xr-xtests/topotests/example-topojson-test/test_topo_json_single_link_loopback/test_example_topojson.py2
-rw-r--r--tests/topotests/lib/bgp.py559
-rw-r--r--tests/topotests/lib/common_config.py417
-rw-r--r--tests/topotests/lib/topojson.py25
-rw-r--r--tests/topotests/pytest.ini4
22 files changed, 3621 insertions, 452 deletions
diff --git a/tests/bgpd/test_peer_attr.c b/tests/bgpd/test_peer_attr.c
index 78016dc9ce..8e1b62ac15 100644
--- a/tests/bgpd/test_peer_attr.c
+++ b/tests/bgpd/test_peer_attr.c
@@ -1170,7 +1170,7 @@ static void test_peer_attr(struct test *test, struct test_peer_attr *pa)
/* Test Preparation: Switch and activate address-family. */
if (!is_attr_type_global(pa->type)) {
test_log(test, "prepare: switch address-family to [%s]",
- afi_safi_print(pa->afi, pa->safi));
+ get_afi_safi_str(pa->afi, pa->safi, false));
test_execute(test, "address-family %s %s",
str_from_afi(pa->afi), str_from_safi(pa->safi));
test_execute(test, "neighbor %s activate", g->name);
@@ -1237,7 +1237,7 @@ static void test_peer_attr(struct test *test, struct test_peer_attr *pa)
/* Test Preparation: Switch and activate address-family. */
if (!is_attr_type_global(pa->type)) {
test_log(test, "prepare: switch address-family to [%s]",
- afi_safi_print(pa->afi, pa->safi));
+ get_afi_safi_str(pa->afi, pa->safi, false));
test_execute(test, "address-family %s %s",
str_from_afi(pa->afi), str_from_safi(pa->safi));
test_execute(test, "neighbor %s activate", g->name);
@@ -1285,7 +1285,7 @@ static void test_peer_attr(struct test *test, struct test_peer_attr *pa)
/* Test Preparation: Switch and activate address-family. */
if (!is_attr_type_global(pa->type)) {
test_log(test, "prepare: switch address-family to [%s]",
- afi_safi_print(pa->afi, pa->safi));
+ get_afi_safi_str(pa->afi, pa->safi, false));
test_execute(test, "address-family %s %s",
str_from_afi(pa->afi), str_from_safi(pa->safi));
test_execute(test, "neighbor %s activate", g->name);
diff --git a/tests/lib/test_privs.c b/tests/lib/test_privs.c
index fc3d908661..de638bc67a 100644
--- a/tests/lib/test_privs.c
+++ b/tests/lib/test_privs.c
@@ -113,7 +113,7 @@ int main(int argc, char **argv)
((test_privs.current_state() == ZPRIVS_RAISED) ? "Raised" : "Lowered")
printf("%s\n", PRIV_STATE());
- frr_elevate_privs(&test_privs) {
+ frr_with_privs(&test_privs) {
printf("%s\n", PRIV_STATE());
}
@@ -125,7 +125,7 @@ int main(int argc, char **argv)
/* but these should continue to work... */
printf("%s\n", PRIV_STATE());
- frr_elevate_privs(&test_privs) {
+ frr_with_privs(&test_privs) {
printf("%s\n", PRIV_STATE());
}
diff --git a/tests/topotests/Dockerfile b/tests/topotests/Dockerfile
index ea6fa4b9e0..4602688782 100644
--- a/tests/topotests/Dockerfile
+++ b/tests/topotests/Dockerfile
@@ -40,12 +40,12 @@ RUN export DEBIAN_FRONTEND=noninteractive \
pytest
RUN cd /tmp \
- && wget -q https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-1/Ubuntu-18.04-x86_64-Packages/libyang-dev_0.16.46_amd64.deb \
+ && wget -q https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/Debian-AMD64-Packages/libyang-dev_0.16.105-1_amd64.deb \
-O libyang-dev.deb \
- && wget -q https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-1/Ubuntu-18.04-x86_64-Packages/libyang_0.16.46_amd64.deb \
+ && wget -q https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/Debian-AMD64-Packages/libyang0.16_0.16.105-1_amd64.deb \
-O libyang.deb \
- && echo "039252cc66eb254a97e160b1c325af669470cde8a02d73ec9f7b920ed3c7997c libyang.deb" | sha256sum -c - \
- && echo "e7e2d5bfc7b33b3218df8bef404432970f9b4ad10d6dbbdcb0e0be2babbb68e9 libyang-dev.deb" | sha256sum -c - \
+ && echo "34bef017e527a590020185f05dc39203bdf1c86223e0d990839623ec629d8598 libyang.deb" | sha256sum -c - \
+ && echo "fe9cc6e3b173ca56ef49428c281e96bf76c0f910aa75cf85098076411484e8f4 libyang-dev.deb" | sha256sum -c - \
&& dpkg -i libyang*.deb \
&& rm libyang*.deb
diff --git a/tests/topotests/all-protocol-startup/r1/ipv4_routes.ref b/tests/topotests/all-protocol-startup/r1/ipv4_routes.ref
index 1fb70a0e2f..0a20231371 100644
--- a/tests/topotests/all-protocol-startup/r1/ipv4_routes.ref
+++ b/tests/topotests/all-protocol-startup/r1/ipv4_routes.ref
@@ -13,6 +13,8 @@ O 192.168.3.0/26 [110/10] is directly connected, r1-eth3, XX:XX:XX
S>* 4.5.6.10/32 [1/0] via 192.168.0.2, r1-eth0, XX:XX:XX
S>* 4.5.6.11/32 [1/0] via 192.168.0.2, r1-eth0, XX:XX:XX
S>* 4.5.6.12/32 [1/0] is directly connected, r1-eth0, XX:XX:XX
+S>* 4.5.6.13/32 [1/0] unreachable (blackhole), XX:XX:XX
+S>* 4.5.6.14/32 [1/0] unreachable (blackhole), XX:XX:XX
S>* 4.5.6.7/32 [1/0] unreachable (blackhole), XX:XX:XX
S>* 4.5.6.8/32 [1/0] unreachable (blackhole), XX:XX:XX
S>* 4.5.6.9/32 [1/0] unreachable (ICMP unreachable), XX:XX:XX
diff --git a/tests/topotests/all-protocol-startup/r1/zebra.conf b/tests/topotests/all-protocol-startup/r1/zebra.conf
index 30e05f01e3..c621593ef7 100644
--- a/tests/topotests/all-protocol-startup/r1/zebra.conf
+++ b/tests/topotests/all-protocol-startup/r1/zebra.conf
@@ -9,6 +9,9 @@ ip route 4.5.6.8/32 Null0
ipv6 route 4:5::6:8/128 Null0
ip route 4.5.6.9/32 reject
ipv6 route 4:5::6:9/128 reject
+# Test various spellings of NULL0 to make sure we accept them
+ip route 4.5.6.13/32 null0
+ip route 4.5.6.14/32 NULL0
# Create normal gateway routes
ip route 4.5.6.10/32 192.168.0.2
ipv6 route 4:5::6:10/128 fc00:0:0:0::2
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 095ebe3344..e99111d90b 100755
--- a/tests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py
+++ b/tests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py
@@ -375,7 +375,7 @@ def test_static_routes(request):
# Verifying RIB routes
dut = 'r3'
protocol = 'bgp'
- next_hop = '10.0.0.2'
+ next_hop = ['10.0.0.2', '10.0.0.5']
result = verify_rib(tgen, 'ipv4', dut, input_dict, next_hop=next_hop,
protocol=protocol)
assert result is True, "Testcase {} :Failed \n Error: {}". \
diff --git a/tests/topotests/bgp-ecmp-topo2/ebgp_ecmp_topo2.json b/tests/topotests/bgp-ecmp-topo2/ebgp_ecmp_topo2.json
new file mode 100755
index 0000000000..50797f130a
--- /dev/null
+++ b/tests/topotests/bgp-ecmp-topo2/ebgp_ecmp_topo2.json
@@ -0,0 +1,664 @@
+{
+ "address_types": [
+ "ipv4",
+ "ipv6"
+ ],
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:DB8:F::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link8": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link9": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link10": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link11": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link12": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link13": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link14": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link15": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link16": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link17": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link18": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link19": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link20": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link21": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link22": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link23": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link24": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link25": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link26": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link27": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link28": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link29": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link30": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link31": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link32": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "next_hop_self": true
+ },
+ "r2-link2": {
+ "next_hop_self": true
+ },
+ "r2-link3": {
+ "next_hop_self": true
+ },
+ "r2-link4": {
+ "next_hop_self": true
+ },
+ "r2-link5": {
+ "next_hop_self": true
+ },
+ "r2-link6": {
+ "next_hop_self": true
+ },
+ "r2-link7": {
+ "next_hop_self": true
+ },
+ "r2-link8": {
+ "next_hop_self": true
+ },
+ "r2-link9": {
+ "next_hop_self": true
+ },
+ "r2-link10": {
+ "next_hop_self": true
+ },
+ "r2-link11": {
+ "next_hop_self": true
+ },
+ "r2-link12": {
+ "next_hop_self": true
+ },
+ "r2-link13": {
+ "next_hop_self": true
+ },
+ "r2-link14": {
+ "next_hop_self": true
+ },
+ "r2-link15": {
+ "next_hop_self": true
+ },
+ "r2-link16": {
+ "next_hop_self": true
+ },
+ "r2-link17": {
+ "next_hop_self": true
+ },
+ "r2-link18": {
+ "next_hop_self": true
+ },
+ "r2-link19": {
+ "next_hop_self": true
+ },
+ "r2-link20": {
+ "next_hop_self": true
+ },
+ "r2-link21": {
+ "next_hop_self": true
+ },
+ "r2-link22": {
+ "next_hop_self": true
+ },
+ "r2-link23": {
+ "next_hop_self": true
+ },
+ "r2-link24": {
+ "next_hop_self": true
+ },
+ "r2-link25": {
+ "next_hop_self": true
+ },
+ "r2-link26": {
+ "next_hop_self": true
+ },
+ "r2-link27": {
+ "next_hop_self": true
+ },
+ "r2-link28": {
+ "next_hop_self": true
+ },
+ "r2-link29": {
+ "next_hop_self": true
+ },
+ "r2-link30": {
+ "next_hop_self": true
+ },
+ "r2-link31": {
+ "next_hop_self": true
+ },
+ "r2-link32": {
+ "next_hop_self": true
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "next_hop_self": true
+ },
+ "r2-link2": {
+ "next_hop_self": true
+ },
+ "r2-link3": {
+ "next_hop_self": true
+ },
+ "r2-link4": {
+ "next_hop_self": true
+ },
+ "r2-link5": {
+ "next_hop_self": true
+ },
+ "r2-link6": {
+ "next_hop_self": true
+ },
+ "r2-link7": {
+ "next_hop_self": true
+ },
+ "r2-link8": {
+ "next_hop_self": true
+ },
+ "r2-link9": {
+ "next_hop_self": true
+ },
+ "r2-link10": {
+ "next_hop_self": true
+ },
+ "r2-link11": {
+ "next_hop_self": true
+ },
+ "r2-link12": {
+ "next_hop_self": true
+ },
+ "r2-link13": {
+ "next_hop_self": true
+ },
+ "r2-link14": {
+ "next_hop_self": true
+ },
+ "r2-link15": {
+ "next_hop_self": true
+ },
+ "r2-link16": {
+ "next_hop_self": true
+ },
+ "r2-link17": {
+ "next_hop_self": true
+ },
+ "r2-link18": {
+ "next_hop_self": true
+ },
+ "r2-link19": {
+ "next_hop_self": true
+ },
+ "r2-link20": {
+ "next_hop_self": true
+ },
+ "r2-link21": {
+ "next_hop_self": true
+ },
+ "r2-link22": {
+ "next_hop_self": true
+ },
+ "r2-link23": {
+ "next_hop_self": true
+ },
+ "r2-link24": {
+ "next_hop_self": true
+ },
+ "r2-link25": {
+ "next_hop_self": true
+ },
+ "r2-link26": {
+ "next_hop_self": true
+ },
+ "r2-link27": {
+ "next_hop_self": true
+ },
+ "r2-link28": {
+ "next_hop_self": true
+ },
+ "r2-link29": {
+ "next_hop_self": true
+ },
+ "r2-link30": {
+ "next_hop_self": true
+ },
+ "r2-link31": {
+ "next_hop_self": true
+ },
+ "r2-link32": {
+ "next_hop_self": true
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link8": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link9": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link10": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link11": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link12": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link13": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link14": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link15": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link16": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link17": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link18": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link19": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link20": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link21": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link22": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link23": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link24": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link25": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link26": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link27": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link28": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link29": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link30": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link31": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link32": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "maximum_paths": {
+ "ebgp": 32
+ },
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {},
+ "r3-link2": {},
+ "r3-link3": {},
+ "r3-link4": {},
+ "r3-link5": {},
+ "r3-link6": {},
+ "r3-link7": {},
+ "r3-link8": {},
+ "r3-link9": {},
+ "r3-link10": {},
+ "r3-link11": {},
+ "r3-link12": {},
+ "r3-link13": {},
+ "r3-link14": {},
+ "r3-link15": {},
+ "r3-link16": {},
+ "r3-link17": {},
+ "r3-link18": {},
+ "r3-link19": {},
+ "r3-link20": {},
+ "r3-link21": {},
+ "r3-link22": {},
+ "r3-link23": {},
+ "r3-link24": {},
+ "r3-link25": {},
+ "r3-link26": {},
+ "r3-link27": {},
+ "r3-link28": {},
+ "r3-link29": {},
+ "r3-link30": {},
+ "r3-link31": {},
+ "r3-link32": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "maximum_paths": {
+ "ebgp": 32
+ },
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {},
+ "r3-link2": {},
+ "r3-link3": {},
+ "r3-link4": {},
+ "r3-link5": {},
+ "r3-link6": {},
+ "r3-link7": {},
+ "r3-link8": {},
+ "r3-link9": {},
+ "r3-link10": {},
+ "r3-link11": {},
+ "r3-link12": {},
+ "r3-link13": {},
+ "r3-link14": {},
+ "r3-link15": {},
+ "r3-link16": {},
+ "r3-link17": {},
+ "r3-link18": {},
+ "r3-link19": {},
+ "r3-link20": {},
+ "r3-link21": {},
+ "r3-link22": {},
+ "r3-link23": {},
+ "r3-link24": {},
+ "r3-link25": {},
+ "r3-link26": {},
+ "r3-link27": {},
+ "r3-link28": {},
+ "r3-link29": {},
+ "r3-link30": {},
+ "r3-link31": {},
+ "r3-link32": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp-ecmp-topo2/ibgp_ecmp_topo2.json b/tests/topotests/bgp-ecmp-topo2/ibgp_ecmp_topo2.json
new file mode 100755
index 0000000000..9010b8c273
--- /dev/null
+++ b/tests/topotests/bgp-ecmp-topo2/ibgp_ecmp_topo2.json
@@ -0,0 +1,674 @@
+{
+ "address_types": [
+ "ipv4",
+ "ipv6"
+ ],
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:DB8:F::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link8": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link9": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link10": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link11": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link12": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link13": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link14": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link15": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link16": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link17": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link18": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link19": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link20": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link21": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link22": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link23": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link24": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link25": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link26": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link27": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link28": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link29": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link30": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link31": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link32": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "next_hop_self": true
+ },
+ "r2-link2": {
+ "next_hop_self": true
+ },
+ "r2-link3": {
+ "next_hop_self": true
+ },
+ "r2-link4": {
+ "next_hop_self": true
+ },
+ "r2-link5": {
+ "next_hop_self": true
+ },
+ "r2-link6": {
+ "next_hop_self": true
+ },
+ "r2-link7": {
+ "next_hop_self": true
+ },
+ "r2-link8": {
+ "next_hop_self": true
+ },
+ "r2-link9": {
+ "next_hop_self": true
+ },
+ "r2-link10": {
+ "next_hop_self": true
+ },
+ "r2-link11": {
+ "next_hop_self": true
+ },
+ "r2-link12": {
+ "next_hop_self": true
+ },
+ "r2-link13": {
+ "next_hop_self": true
+ },
+ "r2-link14": {
+ "next_hop_self": true
+ },
+ "r2-link15": {
+ "next_hop_self": true
+ },
+ "r2-link16": {
+ "next_hop_self": true
+ },
+ "r2-link17": {
+ "next_hop_self": true
+ },
+ "r2-link18": {
+ "next_hop_self": true
+ },
+ "r2-link19": {
+ "next_hop_self": true
+ },
+ "r2-link20": {
+ "next_hop_self": true
+ },
+ "r2-link21": {
+ "next_hop_self": true
+ },
+ "r2-link22": {
+ "next_hop_self": true
+ },
+ "r2-link23": {
+ "next_hop_self": true
+ },
+ "r2-link24": {
+ "next_hop_self": true
+ },
+ "r2-link25": {
+ "next_hop_self": true
+ },
+ "r2-link26": {
+ "next_hop_self": true
+ },
+ "r2-link27": {
+ "next_hop_self": true
+ },
+ "r2-link28": {
+ "next_hop_self": true
+ },
+ "r2-link29": {
+ "next_hop_self": true
+ },
+ "r2-link30": {
+ "next_hop_self": true
+ },
+ "r2-link31": {
+ "next_hop_self": true
+ },
+ "r2-link32": {
+ "next_hop_self": true
+ }
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "next_hop_self": true
+ },
+ "r2-link2": {
+ "next_hop_self": true
+ },
+ "r2-link3": {
+ "next_hop_self": true
+ },
+ "r2-link4": {
+ "next_hop_self": true
+ },
+ "r2-link5": {
+ "next_hop_self": true
+ },
+ "r2-link6": {
+ "next_hop_self": true
+ },
+ "r2-link7": {
+ "next_hop_self": true
+ },
+ "r2-link8": {
+ "next_hop_self": true
+ },
+ "r2-link9": {
+ "next_hop_self": true
+ },
+ "r2-link10": {
+ "next_hop_self": true
+ },
+ "r2-link11": {
+ "next_hop_self": true
+ },
+ "r2-link12": {
+ "next_hop_self": true
+ },
+ "r2-link13": {
+ "next_hop_self": true
+ },
+ "r2-link14": {
+ "next_hop_self": true
+ },
+ "r2-link15": {
+ "next_hop_self": true
+ },
+ "r2-link16": {
+ "next_hop_self": true
+ },
+ "r2-link17": {
+ "next_hop_self": true
+ },
+ "r2-link18": {
+ "next_hop_self": true
+ },
+ "r2-link19": {
+ "next_hop_self": true
+ },
+ "r2-link20": {
+ "next_hop_self": true
+ },
+ "r2-link21": {
+ "next_hop_self": true
+ },
+ "r2-link22": {
+ "next_hop_self": true
+ },
+ "r2-link23": {
+ "next_hop_self": true
+ },
+ "r2-link24": {
+ "next_hop_self": true
+ },
+ "r2-link25": {
+ "next_hop_self": true
+ },
+ "r2-link26": {
+ "next_hop_self": true
+ },
+ "r2-link27": {
+ "next_hop_self": true
+ },
+ "r2-link28": {
+ "next_hop_self": true
+ },
+ "r2-link29": {
+ "next_hop_self": true
+ },
+ "r2-link30": {
+ "next_hop_self": true
+ },
+ "r2-link31": {
+ "next_hop_self": true
+ },
+ "r2-link32": {
+ "next_hop_self": true
+ }
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link8": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link9": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link10": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link11": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link12": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link13": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link14": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link15": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link16": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link17": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link18": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link19": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link20": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link21": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link22": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link23": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link24": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link25": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link26": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link27": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link28": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link29": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link30": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link31": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link32": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "maximum_paths": {
+ "ibgp": 32
+ },
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {},
+ "r3-link2": {},
+ "r3-link3": {},
+ "r3-link4": {},
+ "r3-link5": {},
+ "r3-link6": {},
+ "r3-link7": {},
+ "r3-link8": {},
+ "r3-link9": {},
+ "r3-link10": {},
+ "r3-link11": {},
+ "r3-link12": {},
+ "r3-link13": {},
+ "r3-link14": {},
+ "r3-link15": {},
+ "r3-link16": {},
+ "r3-link17": {},
+ "r3-link18": {},
+ "r3-link19": {},
+ "r3-link20": {},
+ "r3-link21": {},
+ "r3-link22": {},
+ "r3-link23": {},
+ "r3-link24": {},
+ "r3-link25": {},
+ "r3-link26": {},
+ "r3-link27": {},
+ "r3-link28": {},
+ "r3-link29": {},
+ "r3-link30": {},
+ "r3-link31": {},
+ "r3-link32": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "maximum_paths": {
+ "ibgp": 32
+ },
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {},
+ "r3-link2": {},
+ "r3-link3": {},
+ "r3-link4": {},
+ "r3-link5": {},
+ "r3-link6": {},
+ "r3-link7": {},
+ "r3-link8": {},
+ "r3-link9": {},
+ "r3-link10": {},
+ "r3-link11": {},
+ "r3-link12": {},
+ "r3-link13": {},
+ "r3-link14": {},
+ "r3-link15": {},
+ "r3-link16": {},
+ "r3-link17": {},
+ "r3-link18": {},
+ "r3-link19": {},
+ "r3-link20": {},
+ "r3-link21": {},
+ "r3-link22": {},
+ "r3-link23": {},
+ "r3-link24": {},
+ "r3-link25": {},
+ "r3-link26": {},
+ "r3-link27": {},
+ "r3-link28": {},
+ "r3-link29": {},
+ "r3-link30": {},
+ "r3-link31": {},
+ "r3-link32": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py b/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py
new file mode 100755
index 0000000000..4b9f419bf2
--- /dev/null
+++ b/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py
@@ -0,0 +1,817 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2019 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.
+#
+
+
+"""
+Following tests are covered to test ecmp functionality on EBGP.
+1. Verify routes installed as per maximum-paths configuration (8/16/32)
+2. Disable/Shut selected paths nexthops and verify other next are installed in
+ the RIB of DUT. Enable interfaces and verify RIB count.
+3. Verify BGP table and RIB in DUT after clear BGP routes and neighbors.
+4. Verify routes are cleared from BGP and RIB table of DUT when
+ redistribute static configuration is removed.
+5. Shut BGP neigbors one by one and verify BGP and routing table updated
+ accordingly in DUT
+6. Delete static routes and verify routers are cleared from BGP table and RIB
+ of DUT.
+7. Verify routes are cleared from BGP and RIB table of DUT when advertise
+ network configuration is removed.
+"""
+import os
+import sys
+import time
+import json
+import pytest
+# 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, '../../'))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from mininet.topo import Topo
+
+from lib.common_config import (
+ start_topology, write_test_header,
+ write_test_footer,
+ verify_rib, create_static_routes, check_address_types,
+ interface_status, reset_config_on_routers
+)
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence, create_router_bgp,
+ clear_bgp_and_verify)
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+# Reading the data from JSON File for topology and configuration creation
+jsonFile = "{}/ebgp_ecmp_topo2.json".format(CWD)
+
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+# Global variables
+NEXT_HOPS = {"ipv4": [], "ipv6": []}
+INTF_LIST_R3 = []
+INTF_LIST_R2 = []
+NETWORK = {"ipv4": "11.0.20.1/32", "ipv6": "1::/64"}
+NEXT_HOP_IP = {"ipv4": "10.0.0.1", "ipv6": "fd00::1"}
+BGP_CONVERGENCE = False
+
+
+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 NEXT_HOPS, INTF_LIST_R3, INTF_LIST_R2, TEST_STATIC
+ global ADDR_TYPES
+
+ 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__)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen)
+
+ # 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)
+
+ # tgen.mininet_cli()
+ # Api call verify whether BGP is converged
+ ADDR_TYPES = check_address_types()
+
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, ("setup_module :Failed \n Error:"
+ " {}".format(BGP_CONVERGENCE))
+
+ link_data = [val for links, val in
+ topo["routers"]["r2"]["links"].iteritems()
+ if "r3" in links]
+ for adt in ADDR_TYPES:
+ NEXT_HOPS[adt] = [val[adt].split("/")[0] for val in link_data]
+ if adt == "ipv4":
+ NEXT_HOPS[adt] = sorted(
+ NEXT_HOPS[adt], key=lambda x: int(x.split(".")[2]))
+ elif adt == "ipv6":
+ NEXT_HOPS[adt] = sorted(
+ NEXT_HOPS[adt], key=lambda x: int(x.split(':')[-3], 16))
+
+ INTF_LIST_R2 = [val["interface"].split("/")[0] for val in link_data]
+ INTF_LIST_R2 = sorted(INTF_LIST_R2, key=lambda x: int(x.split("eth")[1]))
+
+ link_data = [val for links, val in
+ topo["routers"]["r3"]["links"].iteritems()
+ if "r2" in links]
+ INTF_LIST_R3 = [val["interface"].split("/")[0] for val in link_data]
+ INTF_LIST_R3 = sorted(INTF_LIST_R3, key=lambda x: int(x.split("eth")[1]))
+
+ # STATIC_ROUTE = True
+ logger.info("Running setup_module() done")
+
+
+def teardown_module():
+ """
+ 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()
+
+
+def static_or_nw(tgen, topo, tc_name, test_type, dut):
+
+ if test_type == "redist_static":
+ input_dict_static = {
+ dut: {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"],
+ "next_hop": NEXT_HOP_IP["ipv4"]
+ },
+ {
+ "network": NETWORK["ipv6"],
+ "next_hop": NEXT_HOP_IP["ipv6"]
+ }
+ ]
+ }
+ }
+ logger.info("Configuring static route on router %s", dut)
+ result = create_static_routes(tgen, input_dict_static)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ input_dict_2 = {
+ dut: {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{
+ "redist_type": "static"
+ }]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [{
+ "redist_type": "static"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ logger.info("Configuring redistribute static route on router %s", dut)
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ elif test_type == "advertise_nw":
+ input_dict_nw = {
+ dut: {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": NETWORK["ipv4"]}
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": NETWORK["ipv6"]}
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ logger.info("Advertising networks %s %s from router %s",
+ NETWORK["ipv4"], NETWORK["ipv6"], dut)
+ result = create_router_bgp(tgen, topo, input_dict_nw)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+
+@pytest.mark.parametrize("ecmp_num", ["8", "16", "32"])
+@pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"])
+def test_modify_ecmp_max_paths(request, ecmp_num, test_type):
+ """
+ Verify routes installed as per maximum-paths
+ configuration (8/16/32).
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ reset_config_on_routers(tgen)
+
+ static_or_nw(tgen, topo, tc_name, test_type, "r2")
+
+ input_dict = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "maximum_paths": {
+ "ebgp": ecmp_num,
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "maximum_paths": {
+ "ebgp": ecmp_num,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ logger.info("Configuring bgp maximum-paths %s on router r3", ecmp_num)
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict_1,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ecmp_after_clear_bgp(request):
+ """ Verify BGP table and RIB in DUT after clear BGP routes and neighbors"""
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ reset_config_on_routers(tgen)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+
+ static_or_nw(tgen, topo, tc_name, "redist_static", "r2")
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict_1,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ # Clear bgp
+ result = clear_bgp_and_verify(tgen, topo, dut)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict_1,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ecmp_remove_redistribute_static(request):
+ """ Verify routes are cleared from BGP and RIB table of DUT when
+ redistribute static configuration is removed."""
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ reset_config_on_routers(tgen)
+ static_or_nw(tgen, topo, tc_name, "redist_static", "r2")
+ for addr_type in ADDR_TYPES:
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict_1,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{
+ "redist_type": "static",
+ "delete": True
+
+ }]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [{
+ "redist_type": "static",
+ "delete": True
+
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ logger.info("Remove redistribute static")
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+
+ logger.info("Verifying %s routes on r3 are deleted", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict_1,
+ next_hop=[], protocol=protocol, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Routes still" \
+ " present in RIB".format(tc_name)
+
+ logger.info("Enable redistribute static")
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{
+ "redist_type": "static"
+ }]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [{
+ "redist_type": "static"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict_1,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ecmp_shut_bgp_neighbor(request):
+ """
+ Disable/Shut selected paths nexthops and verify other next are installed in
+ the RIB of DUT. Enable interfaces and verify RIB count.
+
+ Shut BGP neigbors one by one and verify BGP and routing table updated
+ accordingly in DUT
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ logger.info(INTF_LIST_R2)
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+
+ reset_config_on_routers(tgen)
+ static_or_nw(tgen, topo, tc_name, "redist_static", "r2")
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ for intf_num in range(len(INTF_LIST_R2)+1, 16):
+ intf_val = INTF_LIST_R2[intf_num:intf_num+16]
+
+ input_dict_1 = {
+ "r2": {
+ "interface_list": [intf_val],
+ "status": "down"
+ }
+ }
+ logger.info("Shutting down neighbor interface {} on r2".
+ format(intf_val))
+ result = interface_status(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ if intf_num + 16 < 32:
+ check_hops = NEXT_HOPS[addr_type]
+ else:
+ check_hops = []
+
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict,
+ next_hop=check_hops,
+ protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ input_dict_1 = {
+ "r2": {
+ "interface_list": INTF_LIST_R2,
+ "status": "up"
+ }
+ }
+
+ logger.info("Enabling all neighbor interface {} on r2")
+ result = interface_status(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ static_or_nw(tgen, topo, tc_name, "redist_static", "r2")
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ecmp_remove_static_route(request):
+ """
+ Delete static routes and verify routers are cleared from BGP table,
+ and RIB of DUT.
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+
+ reset_config_on_routers(tgen)
+
+ static_or_nw(tgen, topo, tc_name, "redist_static", "r2")
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_1,
+ next_hop=NEXT_HOPS[addr_type], protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "delete": True
+ }
+ ]
+ }
+ }
+
+ logger.info("Remove static routes")
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ logger.info("Verifying %s routes on r3 are removed", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict_2,
+ next_hop=[], protocol=protocol, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Routes still" \
+ " present in RIB".format(tc_name)
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type]
+ }
+ ]
+ }
+ }
+
+ logger.info("Enable static route")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict_4,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+
+def test_ecmp_remove_nw_advertise(request):
+ """
+ Verify routes are cleared from BGP and RIB table of DUT,
+ when advertise network configuration is removed
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+
+ reset_config_on_routers(tgen)
+ static_or_nw(tgen, topo, tc_name, "advertise_nw", "r2")
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ input_dict_3 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [{
+ "network": NETWORK["ipv4"],
+ "delete": True
+ }]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [{
+ "network": NETWORK["ipv6"],
+ "delete": True
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ logger.info("Withdraw advertised networks")
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict,
+ next_hop=[], protocol=protocol, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Routes still" \
+ " present in RIB".format(tc_name)
+
+ static_or_nw(tgen, topo, tc_name, "advertise_nw", "r2")
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict,
+ next_hop=NEXT_HOPS[addr_type],
+ 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/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py b/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py
new file mode 100755
index 0000000000..a9f18ed1fa
--- /dev/null
+++ b/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py
@@ -0,0 +1,813 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2019 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.
+#
+
+
+"""
+Following tests are covered to test ecmp functionality on EBGP.
+1. Verify routes installed as per maximum-paths configuration (8/16/32)
+2. Disable/Shut selected paths nexthops and verify other next are installed in
+ the RIB of DUT. Enable interfaces and verify RIB count.
+3. Verify BGP table and RIB in DUT after clear BGP routes and neighbors.
+4. Verify routes are cleared from BGP and RIB table of DUT when
+ redistribute static configuration is removed.
+5. Shut BGP neigbors one by one and verify BGP and routing table updated
+ accordingly in DUT
+6. Delete static routes and verify routers are cleared from BGP table and RIB
+ of DUT.
+7. Verify routes are cleared from BGP and RIB table of DUT when advertise
+ network configuration is removed.
+"""
+import os
+import sys
+import time
+import json
+import pytest
+# 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, '../../'))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from mininet.topo import Topo
+
+from lib.common_config import (
+ start_topology, write_test_header,
+ write_test_footer,
+ verify_rib, create_static_routes, check_address_types,
+ interface_status, reset_config_on_routers
+)
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence, create_router_bgp,
+ clear_bgp_and_verify)
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+# Reading the data from JSON File for topology and configuration creation
+jsonFile = "{}/ibgp_ecmp_topo2.json".format(CWD)
+
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+# Global variables
+NEXT_HOPS = {"ipv4": [], "ipv6": []}
+INTF_LIST_R3 = []
+INTF_LIST_R2 = []
+NETWORK = {"ipv4": "11.0.20.1/32", "ipv6": "1::/64"}
+NEXT_HOP_IP = {"ipv4": "10.0.0.1", "ipv6": "fd00::1"}
+BGP_CONVERGENCE = False
+
+
+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 NEXT_HOPS, INTF_LIST_R3, INTF_LIST_R2, TEST_STATIC
+ global ADDR_TYPES
+
+ 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__)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen)
+
+ # 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)
+
+ # tgen.mininet_cli()
+ # Api call verify whether BGP is converged
+ ADDR_TYPES = check_address_types()
+
+ for addr_type in ADDR_TYPES:
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, ("setup_module :Failed \n Error:"
+ " {}".format(BGP_CONVERGENCE))
+
+ link_data = [val for links, val in
+ topo["routers"]["r2"]["links"].iteritems()
+ if "r3" in links]
+ for adt in ADDR_TYPES:
+ NEXT_HOPS[adt] = [val[adt].split("/")[0] for val in link_data]
+ if adt == "ipv4":
+ NEXT_HOPS[adt] = sorted(
+ NEXT_HOPS[adt], key=lambda x: int(x.split(".")[2]))
+ elif adt == "ipv6":
+ NEXT_HOPS[adt] = sorted(
+ NEXT_HOPS[adt], key=lambda x: int(x.split(':')[-3], 16))
+
+ INTF_LIST_R2 = [val["interface"].split("/")[0] for val in link_data]
+ INTF_LIST_R2 = sorted(INTF_LIST_R2, key=lambda x: int(x.split("eth")[1]))
+
+ link_data = [val for links, val in
+ topo["routers"]["r3"]["links"].iteritems()
+ if "r2" in links]
+ INTF_LIST_R3 = [val["interface"].split("/")[0] for val in link_data]
+ INTF_LIST_R3 = sorted(INTF_LIST_R3, key=lambda x: int(x.split("eth")[1]))
+
+ # STATIC_ROUTE = True
+ logger.info("Running setup_module() done")
+
+
+def teardown_module():
+ """
+ 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()
+
+
+def static_or_nw(tgen, topo, tc_name, test_type, dut):
+
+ if test_type == "redist_static":
+ input_dict_static = {
+ dut: {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"],
+ "next_hop": NEXT_HOP_IP["ipv4"]
+ },
+ {
+ "network": NETWORK["ipv6"],
+ "next_hop": NEXT_HOP_IP["ipv6"]
+ }
+ ]
+ }
+ }
+ logger.info("Configuring static route on router %s", dut)
+ result = create_static_routes(tgen, input_dict_static)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ input_dict_2 = {
+ dut: {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{
+ "redist_type": "static"
+ }]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [{
+ "redist_type": "static"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ logger.info("Configuring redistribute static route on router %s", dut)
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ elif test_type == "advertise_nw":
+ input_dict_nw = {
+ dut: {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": NETWORK["ipv4"]}
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": NETWORK["ipv6"]}
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ logger.info("Advertising networks %s %s from router %s",
+ NETWORK["ipv4"], NETWORK["ipv6"], dut)
+ result = create_router_bgp(tgen, topo, input_dict_nw)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+
+@pytest.mark.parametrize("ecmp_num", ["8", "16", "32"])
+@pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"])
+def test_modify_ecmp_max_paths(request, ecmp_num, test_type):
+ """
+ Verify routes installed as per maximum-paths
+ configuration (8/16/32).
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ reset_config_on_routers(tgen)
+
+ static_or_nw(tgen, topo, tc_name, test_type, "r2")
+
+ input_dict = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "maximum_paths": {
+ "ibgp": ecmp_num,
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "maximum_paths": {
+ "ibgp": ecmp_num,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ logger.info("Configuring bgp maximum-paths %s on router r3", ecmp_num)
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict_1,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ecmp_after_clear_bgp(request):
+ """ Verify BGP table and RIB in DUT after clear BGP routes and neighbors"""
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ reset_config_on_routers(tgen)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+
+ static_or_nw(tgen, topo, tc_name, "redist_static", "r2")
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict_1,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ # Clear bgp
+ result = clear_bgp_and_verify(tgen, topo, dut)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict_1,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ecmp_remove_redistribute_static(request):
+ """ Verify routes are cleared from BGP and RIB table of DUT when
+ redistribute static configuration is removed."""
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ reset_config_on_routers(tgen)
+ static_or_nw(tgen, topo, tc_name, "redist_static", "r2")
+ for addr_type in ADDR_TYPES:
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict_1,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{
+ "redist_type": "static",
+ "delete": True
+
+ }]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [{
+ "redist_type": "static",
+ "delete": True
+
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ logger.info("Remove redistribute static")
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+
+ logger.info("Verifying %s routes on r3 are deleted", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict_1,
+ next_hop=[], protocol=protocol, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Routes still" \
+ " present in RIB".format(tc_name)
+
+ logger.info("Enable redistribute static")
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{
+ "redist_type": "static"
+ }]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [{
+ "redist_type": "static"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict_1,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ecmp_shut_bgp_neighbor(request):
+ """ Shut BGP neigbors one by one and verify BGP and routing table updated
+ accordingly in DUT """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ logger.info(INTF_LIST_R2)
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+
+ reset_config_on_routers(tgen)
+ static_or_nw(tgen, topo, tc_name, "redist_static", "r2")
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ for intf_num in range(len(INTF_LIST_R2)+1, 16):
+ intf_val = INTF_LIST_R2[intf_num:intf_num+16]
+
+ input_dict_1 = {
+ "r2": {
+ "interface_list": [intf_val],
+ "status": "down"
+ }
+ }
+ logger.info("Shutting down neighbor interface {} on r2".
+ format(intf_val))
+ result = interface_status(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ if intf_num + 16 < 32:
+ check_hops = NEXT_HOPS[addr_type]
+ else:
+ check_hops = []
+
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict,
+ next_hop=check_hops,
+ protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ input_dict_1 = {
+ "r2": {
+ "interface_list": INTF_LIST_R2,
+ "status": "up"
+ }
+ }
+
+ logger.info("Enabling all neighbor interface {} on r2")
+ result = interface_status(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ static_or_nw(tgen, topo, tc_name, "redist_static", "r2")
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ecmp_remove_static_route(request):
+ """
+ Delete static routes and verify routers are cleared from BGP table,
+ and RIB of DUT.
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+
+ reset_config_on_routers(tgen)
+
+ static_or_nw(tgen, topo, tc_name, "redist_static", "r2")
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_1,
+ next_hop=NEXT_HOPS[addr_type], protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "delete": True
+ }
+ ]
+ }
+ }
+
+ logger.info("Remove static routes")
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ logger.info("Verifying %s routes on r3 are removed", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict_2,
+ next_hop=[], protocol=protocol, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Routes still" \
+ " present in RIB".format(tc_name)
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type]
+ }
+ ]
+ }
+ }
+
+ logger.info("Enable static route")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict_4,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ecmp_remove_nw_advertise(request):
+ """
+ Verify routes are cleared from BGP and RIB table of DUT,
+ when advertise network configuration is removed
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+
+ reset_config_on_routers(tgen)
+ static_or_nw(tgen, topo, tc_name, "advertise_nw", "r2")
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ input_dict_3 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [{
+ "network": NETWORK["ipv4"],
+ "delete": True
+ }]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [{
+ "network": NETWORK["ipv6"],
+ "delete": True
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ logger.info("Withdraw advertised networks")
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict,
+ next_hop=[], protocol=protocol, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Routes still" \
+ " present in RIB".format(tc_name)
+
+ static_or_nw(tgen, topo, tc_name, "advertise_nw", "r2")
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type]
+ }
+ ]
+ }
+ }
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(tgen, addr_type, dut, input_dict,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py b/tests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py
index 2b9c411ff2..9f92b4b290 100755
--- a/tests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py
+++ b/tests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py
@@ -219,7 +219,8 @@ def test_next_hop_attribute(request):
dut = "r1"
protocol = "bgp"
result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False)
- assert result is not True
+ assert result is not True, "Testcase {} : Failed \n Error: Routes still" \
+ " present in RIB".format(tc_name)
# Configure next-hop-self to bgp neighbor
input_dict_1 = {
@@ -484,7 +485,7 @@ def test_localpref_attribute(request):
"neighbor": {
"r1": {
"dest_link": {
- "r3": {
+ "r2": {
"route_maps": [
{"name": "RMAP_LOCAL_PREF",
"direction": "in"}
@@ -499,6 +500,7 @@ def test_localpref_attribute(request):
}
}
}
+
result = create_router_bgp(tgen, topo, input_dict_4)
assert result is True, "Testcase {} : Failed \n Error: {}".format(
tc_name, result)
diff --git a/tests/topotests/bgp-prefix-list-topo1/test_prefix_lists.py b/tests/topotests/bgp-prefix-list-topo1/test_prefix_lists.py
index d3892e9d07..b8975997ea 100755
--- a/tests/topotests/bgp-prefix-list-topo1/test_prefix_lists.py
+++ b/tests/topotests/bgp-prefix-list-topo1/test_prefix_lists.py
@@ -386,8 +386,8 @@ def test_ip_prefix_lists_out_permit(request):
tc_name, result)
result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False)
- assert result is not True, "Testcase {} : Failed \n Error: {}".format(
- tc_name, result)
+ assert result is not True, "Testcase {} : Failed \n Error: Routes still" \
+ " present in RIB".format(tc_name)
write_test_footer(tc_name)
@@ -497,8 +497,8 @@ def test_ip_prefix_lists_in_deny_and_permit_any(request):
dut = "r3"
protocol = "bgp"
result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False)
- assert result is not True, "Testcase {} : Failed \n Error: {}".format(
- tc_name, result)
+ assert result is not True, "Testcase {} : Failed \n Error: Routes still" \
+ " present in RIB".format(tc_name)
write_test_footer(tc_name)
@@ -542,7 +542,6 @@ def test_delete_prefix_lists(request):
result = verify_prefix_lists(tgen, input_dict_2)
assert result is not True, "Testcase {} : Failed \n Error: {}".format(
tc_name, result)
- logger.info(result)
# Delete prefix list
input_dict_2 = {
@@ -714,9 +713,8 @@ def test_ip_prefix_lists_out_deny_and_permit_any(request):
dut = "r4"
protocol = "bgp"
result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False)
- assert result is not True, "Testcase {} : Failed \n Error: {}".format(
- tc_name, result)
-
+ assert result is not True, "Testcase {} : Failed \n Error: Routes still" \
+ " present in RIB".format(tc_name)
write_test_footer(tc_name)
@@ -859,8 +857,8 @@ def test_modify_prefix_lists_in_permit_to_deny(request):
dut = "r3"
protocol = "bgp"
result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False)
- assert result is not True, "Testcase {} : Failed \n Error: {}".format(
- tc_name, result)
+ assert result is not True, "Testcase {} : Failed \n Error: Routes still" \
+ " present in RIB".format(tc_name)
write_test_footer(tc_name)
@@ -972,8 +970,8 @@ def test_modify_prefix_lists_in_deny_to_permit(request):
dut = "r3"
protocol = "bgp"
result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False)
- assert result is not True, "Testcase {} : Failed \n Error: {}".format(
- tc_name, result)
+ assert result is not True, "Testcase {} : Failed \n Error: Routes still" \
+ " present in RIB".format(tc_name)
# Modify ip prefix list
input_dict_1 = {
@@ -1152,8 +1150,8 @@ def test_modify_prefix_lists_out_permit_to_deny(request):
dut = "r4"
protocol = "bgp"
result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False)
- assert result is not True, "Testcase {} : Failed \n Error: {}".format(
- tc_name, result)
+ assert result is not True, "Testcase {} : Failed \n Error: Routes still" \
+ " present in RIB".format(tc_name)
write_test_footer(tc_name)
@@ -1265,8 +1263,8 @@ def test_modify_prefix_lists_out_deny_to_permit(request):
dut = "r4"
protocol = "bgp"
result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False)
- assert result is not True, "Testcase {} : Failed \n Error: {}".format(
- tc_name, result)
+ assert result is not True, "Testcase {} : Failed \n Error: Routes still" \
+ " present in RIB".format(tc_name)
# Modify ip prefix list
input_dict_1 = {
@@ -1439,8 +1437,8 @@ def test_ip_prefix_lists_implicit_deny(request):
dut = "r4"
protocol = "bgp"
result = verify_rib(tgen, "ipv4", dut, input_dict_1, protocol=protocol, expected=False)
- assert result is not True, "Testcase {} : Failed \n Error: {}".format(
- tc_name, result)
+ assert result is not True, "Testcase {} : Failed \n Error: Routes still" \
+ " present in RIB".format(tc_name)
write_test_footer(tc_name)
diff --git a/tests/topotests/bgp_comm-list_delete/test_bgp_comm-list_delete.py b/tests/topotests/bgp_comm-list_delete/test_bgp_comm-list_delete.py
index de6c35ba8f..ed350ebfeb 100644
--- a/tests/topotests/bgp_comm-list_delete/test_bgp_comm-list_delete.py
+++ b/tests/topotests/bgp_comm-list_delete/test_bgp_comm-list_delete.py
@@ -88,7 +88,7 @@ def test_bgp_maximum_prefix_invalid():
while True:
output = json.loads(tgen.gears[router].vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
if output['192.168.255.1']['bgpState'] == 'Established':
- if output['192.168.255.1']['addressFamilyInfo']['IPv4 Unicast']['acceptedPrefixCounter'] == 2:
+ if output['192.168.255.1']['addressFamilyInfo']['ipv4Unicast']['acceptedPrefixCounter'] == 2:
return True
def _bgp_comm_list_delete(router):
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/adjacencies.py b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/adjacencies.py
index 7b3a883afa..1317a510d1 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/adjacencies.py
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/adjacencies.py
@@ -2,16 +2,16 @@ from lutil import luCommand
luCommand('ce1','ping 192.168.1.1 -c 1',' 0. packet loss','pass','CE->PE ping')
luCommand('ce2','ping 192.168.1.1 -c 1',' 0. packet loss','pass','CE->PE ping')
luCommand('ce3','ping 192.168.1.1 -c 1',' 0. packet loss','pass','CE->PE ping')
-luCommand('ce1','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up',90)
-luCommand('ce2','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up')
-luCommand('ce3','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up')
+luCommand('ce1','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up',180)
+luCommand('ce2','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up',180)
+luCommand('ce3','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up',180)
luCommand('r1','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60)
luCommand('r3','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60)
luCommand('r4','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60)
-luCommand('r2','vtysh -c "show bgp summary"',' 00:0.* 00:0.* 00:0','wait','Core adjacencies up')
-luCommand('r1','vtysh -c "show bgp summary"',' 00:0','pass','Core adjacencies up')
-luCommand('r3','vtysh -c "show bgp summary"',' 00:0','pass','Core adjacencies up')
-luCommand('r4','vtysh -c "show bgp summary"',' 00:0','pass','Core adjacencies up')
+luCommand('r2','vtysh -c "show bgp summary"',' 00:0.* 00:0.* 00:0','wait','Core adjacencies up',180)
+luCommand('r1','vtysh -c "show bgp summary"',' 00:0','wait','Core adjacencies up',180)
+luCommand('r3','vtysh -c "show bgp summary"',' 00:0','wait','Core adjacencies up',180)
+luCommand('r4','vtysh -c "show bgp summary"',' 00:0','wait','Core adjacencies up',180)
luCommand('r1','vtysh -c "show bgp vrf all summary"',' 00:0.* 00:0','pass','All adjacencies up')
luCommand('r3','vtysh -c "show bgp vrf all summary"',' 00:0.* 00:0','pass','All adjacencies up')
luCommand('r4','vtysh -c "show bgp vrf all summary"',' 00:0.* 00:0','pass','All adjacencies up')
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py
index 5674120b9c..c2b0cf9e7a 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py
@@ -6,10 +6,10 @@ luCommand('ce4','vtysh -c "show bgp vrf all summary"',' 00:0','wait','Adjacencie
luCommand('r1','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60)
luCommand('r3','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60)
luCommand('r4','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60)
-luCommand('r2','vtysh -c "show bgp summary"',' 00:0.* 00:0.* 00:0','wait','Core adjacencies up',300)
-luCommand('r1','vtysh -c "show bgp summary"',' 00:0','pass','Core adjacencies up')
-luCommand('r3','vtysh -c "show bgp summary"',' 00:0','pass','Core adjacencies up')
-luCommand('r4','vtysh -c "show bgp summary"',' 00:0','pass','Core adjacencies up')
+luCommand('r2','vtysh -c "show bgp summary"',' 00:0.* 00:0.* 00:0','wait','Core adjacencies up',180)
+luCommand('r1','vtysh -c "show bgp summary"',' 00:0','wait','Core adjacencies up', 180)
+luCommand('r3','vtysh -c "show bgp summary"',' 00:0','wait','Core adjacencies up', 180)
+luCommand('r4','vtysh -c "show bgp summary"',' 00:0','wait','Core adjacencies up', 180)
luCommand('r1','vtysh -c "show bgp vrf all summary"',' 00:0.* 00:0','pass','All adjacencies up')
luCommand('r3','vtysh -c "show bgp vrf all summary"',' 00:0.* 00:0','pass','All adjacencies up')
luCommand('r4','vtysh -c "show bgp vrf all summary"',' 00:0.* 00:0.* 00:0','pass','All adjacencies up')
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/adjacencies.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/adjacencies.py
index 1f53791f6a..6fbe4ff1c0 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity/scripts/adjacencies.py
+++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/adjacencies.py
@@ -1,10 +1,10 @@
luCommand('r1','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60)
luCommand('r3','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60)
luCommand('r4','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60)
-luCommand('r2','vtysh -c "show bgp summary"',' 00:0.* 00:0.* 00:0','wait','Core adjacencies up',30)
-luCommand('r1','vtysh -c "show bgp vrf all summary"',' 00:0','pass','All adjacencies up')
-luCommand('r3','vtysh -c "show bgp vrf all summary"',' 00:0','pass','All adjacencies up')
-luCommand('r4','vtysh -c "show bgp vrf all summary"',' 00:0','pass','All adjacencies up')
+luCommand('r2','vtysh -c "show bgp summary"',' 00:0.* 00:0.* 00:0','wait','Core adjacencies up',180)
+luCommand('r1','vtysh -c "show bgp vrf all summary"',' 00:0','wait','All adjacencies up',180)
+luCommand('r3','vtysh -c "show bgp vrf all summary"',' 00:0','wait','All adjacencies up',180)
+luCommand('r4','vtysh -c "show bgp vrf all summary"',' 00:0','wait','All adjacencies up',180)
luCommand('r1','ping 3.3.3.3 -c 1',' 0. packet loss','wait','PE->PE3 (loopback) ping')
luCommand('r1','ping 4.4.4.4 -c 1',' 0. packet loss','wait','PE->PE4 (loopback) ping')
#luCommand('r4','ping 3.3.3.3 -c 1',' 0. packet loss','wait','PE->PE3 (loopback) ping')
diff --git a/tests/topotests/docker/inner/compile_frr.sh b/tests/topotests/docker/inner/compile_frr.sh
index 2d72082c1e..dee0ec8118 100755
--- a/tests/topotests/docker/inner/compile_frr.sh
+++ b/tests/topotests/docker/inner/compile_frr.sh
@@ -84,6 +84,7 @@ if [ ! -e Makefile ]; then
--enable-static-bin \
--enable-static \
--enable-shared \
+ --enable-dev-build \
--with-moduledir=/usr/lib/frr/modules \
--prefix=/usr \
--localstatedir=/var/run/frr \
diff --git a/tests/topotests/example-topojson-test/test_topo_json_single_link_loopback/test_example_topojson.py b/tests/topotests/example-topojson-test/test_topo_json_single_link_loopback/test_example_topojson.py
index b794b96a63..cd069aaec5 100755
--- a/tests/topotests/example-topojson-test/test_topo_json_single_link_loopback/test_example_topojson.py
+++ b/tests/topotests/example-topojson-test/test_topo_json_single_link_loopback/test_example_topojson.py
@@ -178,7 +178,7 @@ def test_static_routes(request):
# Static routes are created as part of initial configuration,
# verifying RIB
dut = 'r3'
- next_hop = '10.0.0.1'
+ next_hop = ['10.0.0.1', '10.0.0.5']
input_dict = {
"r1": {
"static_routes": [
diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py
index 2613f45f1c..c47dddb8d4 100644
--- a/tests/topotests/lib/bgp.py
+++ b/tests/topotests/lib/bgp.py
@@ -32,7 +32,8 @@ from lib.common_config import (create_common_configuration,
load_config_to_router,
check_address_types,
generate_ips,
- find_interface_with_greater_ip)
+ find_interface_with_greater_ip,
+ run_frr_cmd, retry)
BGP_CONVERGENCE_TIMEOUT = 10
@@ -116,8 +117,8 @@ def create_router_bgp(tgen, topo, input_dict=None, build=False):
logger.debug("Router %s: 'bgp' not present in input_dict", router)
continue
- result = __create_bgp_global(tgen, input_dict, router, build)
- if result is True:
+ data_all_bgp = __create_bgp_global(tgen, input_dict, router, build)
+ if data_all_bgp:
bgp_data = input_dict[router]["bgp"]
bgp_addr_data = bgp_data.setdefault("address_family", {})
@@ -134,8 +135,18 @@ def create_router_bgp(tgen, topo, input_dict=None, build=False):
or ipv6_data.setdefault("unicast", {}) else False
if neigh_unicast:
- result = __create_bgp_unicast_neighbor(
- tgen, topo, input_dict, router, build)
+ data_all_bgp = __create_bgp_unicast_neighbor(
+ tgen, topo, input_dict, router,
+ config_data=data_all_bgp)
+
+ try:
+ result = create_common_configuration(tgen, router, data_all_bgp,
+ "bgp", build)
+ except InvalidCLIError:
+ # Traceback
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
logger.debug("Exiting lib API: create_router_bgp()")
return result
@@ -157,77 +168,66 @@ def __create_bgp_global(tgen, input_dict, router, build=False):
True or False
"""
- result = False
logger.debug("Entering lib API: __create_bgp_global()")
- try:
-
- bgp_data = input_dict[router]["bgp"]
- del_bgp_action = bgp_data.setdefault("delete", False)
- if del_bgp_action:
- config_data = ["no router bgp"]
- result = create_common_configuration(tgen, router, config_data,
- "bgp", build=build)
- return result
- config_data = []
+ bgp_data = input_dict[router]["bgp"]
+ del_bgp_action = bgp_data.setdefault("delete", False)
+ if del_bgp_action:
+ config_data = ["no router bgp"]
- if "local_as" not in bgp_data and build:
- logger.error("Router %s: 'local_as' not present in input_dict"
- "for BGP", router)
- return False
+ return config_data
- local_as = bgp_data.setdefault("local_as", "")
- cmd = "router bgp {}".format(local_as)
- vrf_id = bgp_data.setdefault("vrf", None)
- if vrf_id:
- cmd = "{} vrf {}".format(cmd, vrf_id)
-
- config_data.append(cmd)
+ config_data = []
- router_id = bgp_data.setdefault("router_id", None)
- del_router_id = bgp_data.setdefault("del_router_id", False)
- if del_router_id:
- config_data.append("no bgp router-id")
- if router_id:
- config_data.append("bgp router-id {}".format(
- router_id))
+ if "local_as" not in bgp_data and build:
+ logger.error("Router %s: 'local_as' not present in input_dict"
+ "for BGP", router)
+ return False
- aggregate_address = bgp_data.setdefault("aggregate_address",
- {})
- if aggregate_address:
- network = aggregate_address.setdefault("network", None)
- if not network:
- logger.error("Router %s: 'network' not present in "
- "input_dict for BGP", router)
- else:
- cmd = "aggregate-address {}".format(network)
+ local_as = bgp_data.setdefault("local_as", "")
+ cmd = "router bgp {}".format(local_as)
+ vrf_id = bgp_data.setdefault("vrf", None)
+ if vrf_id:
+ cmd = "{} vrf {}".format(cmd, vrf_id)
+
+ config_data.append(cmd)
+
+ router_id = bgp_data.setdefault("router_id", None)
+ del_router_id = bgp_data.setdefault("del_router_id", False)
+ if del_router_id:
+ config_data.append("no bgp router-id")
+ if router_id:
+ config_data.append("bgp router-id {}".format(
+ router_id))
+
+ aggregate_address = bgp_data.setdefault("aggregate_address",
+ {})
+ if aggregate_address:
+ network = aggregate_address.setdefault("network", None)
+ if not network:
+ logger.error("Router %s: 'network' not present in "
+ "input_dict for BGP", router)
+ else:
+ cmd = "aggregate-address {}".format(network)
- as_set = aggregate_address.setdefault("as_set", False)
- summary = aggregate_address.setdefault("summary", False)
- del_action = aggregate_address.setdefault("delete", False)
- if as_set:
- cmd = "{} {}".format(cmd, "as-set")
- if summary:
- cmd = "{} {}".format(cmd, "summary")
+ as_set = aggregate_address.setdefault("as_set", False)
+ summary = aggregate_address.setdefault("summary", False)
+ del_action = aggregate_address.setdefault("delete", False)
+ if as_set:
+ cmd = "{} {}".format(cmd, "as-set")
+ if summary:
+ cmd = "{} {}".format(cmd, "summary")
- if del_action:
- cmd = "no {}".format(cmd)
+ if del_action:
+ cmd = "no {}".format(cmd)
- config_data.append(cmd)
+ config_data.append(cmd)
- result = create_common_configuration(tgen, router, config_data,
- "bgp", build=build)
- except InvalidCLIError:
- # Traceback
- errormsg = traceback.format_exc()
- logger.error(errormsg)
- return errormsg
-
- logger.debug("Exiting lib API: create_bgp_global()")
- return result
+ return config_data
-def __create_bgp_unicast_neighbor(tgen, topo, input_dict, router, build=False):
+def __create_bgp_unicast_neighbor(tgen, topo, input_dict, router,
+ config_data=None):
"""
Helper API to create configuration for address-family unicast
@@ -240,124 +240,118 @@ def __create_bgp_unicast_neighbor(tgen, topo, input_dict, router, build=False):
* `build` : Only for initial setup phase this is set as True.
"""
- result = False
logger.debug("Entering lib API: __create_bgp_unicast_neighbor()")
- try:
- config_data = ["router bgp"]
- bgp_data = input_dict[router]["bgp"]["address_family"]
- for addr_type, addr_dict in bgp_data.iteritems():
- if not addr_dict:
- continue
+ add_neigh = True
+ if "router bgp "in config_data:
+ add_neigh = False
+ bgp_data = input_dict[router]["bgp"]["address_family"]
- if not check_address_types(addr_type):
- continue
+ for addr_type, addr_dict in bgp_data.iteritems():
+ if not addr_dict:
+ continue
+ if not check_address_types(addr_type):
+ continue
+
+ addr_data = addr_dict["unicast"]
+ if addr_data:
config_data.append("address-family {} unicast".format(
addr_type
))
- addr_data = addr_dict["unicast"]
- advertise_network = addr_data.setdefault("advertise_networks",
- [])
- for advertise_network_dict in advertise_network:
- network = advertise_network_dict["network"]
- if type(network) is not list:
- network = [network]
-
- if "no_of_network" in advertise_network_dict:
- no_of_network = advertise_network_dict["no_of_network"]
- else:
- no_of_network = 1
-
- del_action = advertise_network_dict.setdefault("delete",
- False)
+ advertise_network = addr_data.setdefault("advertise_networks",
+ [])
+ for advertise_network_dict in advertise_network:
+ network = advertise_network_dict["network"]
+ if type(network) is not list:
+ network = [network]
+
+ if "no_of_network" in advertise_network_dict:
+ no_of_network = advertise_network_dict["no_of_network"]
+ else:
+ no_of_network = 1
- # Generating IPs for verification
- prefix = str(
- ipaddr.IPNetwork(unicode(network[0])).prefixlen)
- network_list = generate_ips(network, no_of_network)
- for ip in network_list:
- ip = str(ipaddr.IPNetwork(unicode(ip)).network)
+ del_action = advertise_network_dict.setdefault("delete",
+ False)
- cmd = "network {}/{}\n".format(ip, prefix)
- if del_action:
- cmd = "no {}".format(cmd)
+ # Generating IPs for verification
+ prefix = str(
+ ipaddr.IPNetwork(unicode(network[0])).prefixlen)
+ network_list = generate_ips(network, no_of_network)
+ for ip in network_list:
+ ip = str(ipaddr.IPNetwork(unicode(ip)).network)
- config_data.append(cmd)
+ cmd = "network {}/{}".format(ip, prefix)
+ if del_action:
+ cmd = "no {}".format(cmd)
- max_paths = addr_data.setdefault("maximum_paths", {})
- if max_paths:
- ibgp = max_paths.setdefault("ibgp", None)
- ebgp = max_paths.setdefault("ebgp", None)
- if ibgp:
- config_data.append("maximum-paths ibgp {}".format(
- ibgp
- ))
- if ebgp:
- config_data.append("maximum-paths {}".format(
- ebgp
- ))
-
- aggregate_address = addr_data.setdefault("aggregate_address",
- {})
- if aggregate_address:
- ip = aggregate_address("network", None)
- attribute = aggregate_address("attribute", None)
- if ip:
- cmd = "aggregate-address {}".format(ip)
- if attribute:
- cmd = "{} {}".format(cmd, attribute)
+ config_data.append(cmd)
- config_data.append(cmd)
+ max_paths = addr_data.setdefault("maximum_paths", {})
+ if max_paths:
+ ibgp = max_paths.setdefault("ibgp", None)
+ ebgp = max_paths.setdefault("ebgp", None)
+ if ibgp:
+ config_data.append("maximum-paths ibgp {}".format(
+ ibgp
+ ))
+ if ebgp:
+ config_data.append("maximum-paths {}".format(
+ ebgp
+ ))
+
+ aggregate_address = addr_data.setdefault("aggregate_address",
+ {})
+ if aggregate_address:
+ ip = aggregate_address("network", None)
+ attribute = aggregate_address("attribute", None)
+ if ip:
+ cmd = "aggregate-address {}".format(ip)
+ if attribute:
+ cmd = "{} {}".format(cmd, attribute)
- redistribute_data = addr_data.setdefault("redistribute", {})
- if redistribute_data:
- for redistribute in redistribute_data:
- if "redist_type" not in redistribute:
- logger.error("Router %s: 'redist_type' not present in "
- "input_dict", router)
- else:
- cmd = "redistribute {}".format(
- redistribute["redist_type"])
- redist_attr = redistribute.setdefault("attribute",
- None)
- if redist_attr:
- cmd = "{} {}".format(cmd, redist_attr)
- del_action = redistribute.setdefault("delete", False)
- if del_action:
- cmd = "no {}".format(cmd)
- config_data.append(cmd)
+ config_data.append(cmd)
- if "neighbor" in addr_data:
- neigh_data = __create_bgp_neighbor(topo, input_dict,
- router, addr_type)
- config_data.extend(neigh_data)
+ redistribute_data = addr_data.setdefault("redistribute", {})
+ if redistribute_data:
+ for redistribute in redistribute_data:
+ if "redist_type" not in redistribute:
+ logger.error("Router %s: 'redist_type' not present in "
+ "input_dict", router)
+ else:
+ cmd = "redistribute {}".format(
+ redistribute["redist_type"])
+ redist_attr = redistribute.setdefault("attribute",
+ None)
+ if redist_attr:
+ cmd = "{} {}".format(cmd, redist_attr)
+ del_action = redistribute.setdefault("delete", False)
+ if del_action:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
- for addr_type, addr_dict in bgp_data.iteritems():
- if not addr_dict or not check_address_types(addr_type):
- continue
+ if "neighbor" in addr_data:
+ neigh_data = __create_bgp_neighbor(topo, input_dict,
+ router, addr_type, add_neigh)
+ config_data.extend(neigh_data)
- addr_data = addr_dict["unicast"]
- if "neighbor" in addr_data:
- neigh_addr_data = __create_bgp_unicast_address_family(
- topo, input_dict, router, addr_type)
+ for addr_type, addr_dict in bgp_data.iteritems():
+ if not addr_dict or not check_address_types(addr_type):
+ continue
- config_data.extend(neigh_addr_data)
+ addr_data = addr_dict["unicast"]
+ if "neighbor" in addr_data:
+ neigh_addr_data = __create_bgp_unicast_address_family(
+ topo, input_dict, router, addr_type, add_neigh)
- result = create_common_configuration(tgen, router, config_data,
- None, build=build)
+ config_data.extend(neigh_addr_data)
- except InvalidCLIError:
- # Traceback
- errormsg = traceback.format_exc()
- logger.error(errormsg)
- return errormsg
logger.debug("Exiting lib API: __create_bgp_unicast_neighbor()")
- return result
+ return config_data
-def __create_bgp_neighbor(topo, input_dict, router, addr_type):
+def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True):
"""
Helper API to create neighbor specific configuration
@@ -391,7 +385,8 @@ def __create_bgp_neighbor(topo, input_dict, router, addr_type):
neigh_cxt = "neighbor {}".format(ip_addr)
- 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))
@@ -429,7 +424,8 @@ def __create_bgp_neighbor(topo, input_dict, router, addr_type):
return config_data
-def __create_bgp_unicast_address_family(topo, input_dict, router, addr_type):
+def __create_bgp_unicast_address_family(topo, input_dict, router, addr_type,
+ add_neigh=True):
"""
API prints bgp global config to bgp_json file.
@@ -531,6 +527,7 @@ def __create_bgp_unicast_address_family(topo, input_dict, router, addr_type):
#############################################
# Verification APIs
#############################################
+@retry(attempts=3, wait=2, return_is_str=True)
def verify_router_id(tgen, topo, input_dict):
"""
Running command "show ip bgp json" for DUT and reading router-id
@@ -565,7 +562,7 @@ def verify_router_id(tgen, topo, input_dict):
errormsg(str) or True
"""
- logger.info("Entering lib API: verify_router_id()")
+ logger.debug("Entering lib API: verify_router_id()")
for router in input_dict.keys():
if router not in tgen.routers():
continue
@@ -576,9 +573,9 @@ def verify_router_id(tgen, topo, input_dict):
"del_router_id", False)
logger.info("Checking router %s router-id", router)
- show_bgp_json = rnode.vtysh_cmd("show ip bgp json",
+ show_bgp_json = run_frr_cmd(rnode, "show bgp summary json",
isjson=True)
- router_id_out = show_bgp_json["routerId"]
+ router_id_out = show_bgp_json["ipv4Unicast"]["routerId"]
router_id_out = ipaddr.IPv4Address(unicode(router_id_out))
# Once router-id is deleted, highest interface ip should become
@@ -598,100 +595,84 @@ def verify_router_id(tgen, topo, input_dict):
router_id_out)
return errormsg
- logger.info("Exiting lib API: verify_router_id()")
+ logger.debug("Exiting lib API: verify_router_id()")
return True
+@retry(attempts=20, wait=2, return_is_str=True)
def verify_bgp_convergence(tgen, topo):
"""
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
* `addr_type`: ip_type, ipv4/ipv6
-
Usage
-----
# To veriry is BGP is converged for all the routers used in
topology
results = verify_bgp_convergence(tgen, topo, "ipv4")
-
Returns
-------
errormsg(str) or True
"""
- logger.info("Entering lib API: verify_bgp_confergence()")
+ logger.debug("Entering lib API: verify_bgp_convergence()")
for router, rnode in tgen.routers().iteritems():
- logger.info("Verifying BGP Convergence on router %s:", router)
-
- for retry in range(1, 11):
- show_bgp_json = rnode.vtysh_cmd("show bgp 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"
- return errormsg
+ logger.info("Verifying BGP Convergence on router %s", router)
+ show_bgp_json = run_frr_cmd(rnode, "show bgp 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"
+ return errormsg
- # To find neighbor ip type
+ # To find neighbor ip type
+ bgp_addr_type = topo["routers"][router]["bgp"]["address_family"]
+ for addr_type in bgp_addr_type.keys():
+ if not check_address_types(addr_type):
+ continue
total_peer = 0
- bgp_addr_type = topo["routers"][router]["bgp"]["address_family"]
- 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 in bgp_neighbors:
- total_peer += len(bgp_neighbors[bgp_neighbor]["dest_link"])
-
- for addr_type in bgp_addr_type.keys():
- bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"]
-
- no_of_peer = 0
- for bgp_neighbor, peer_data in bgp_neighbors.iteritems():
- for dest_link in peer_data["dest_link"].keys():
- data = topo["routers"][bgp_neighbor]["links"]
- if dest_link in data:
- neighbor_ip = \
- data[dest_link][addr_type].split("/")[0]
- if addr_type == "ipv4":
- ipv4_data = show_bgp_json["ipv4Unicast"][
- "peers"]
- nh_state = ipv4_data[neighbor_ip]["state"]
- else:
- ipv6_data = show_bgp_json["ipv6Unicast"][
- "peers"]
- nh_state = ipv6_data[neighbor_ip]["state"]
-
- if nh_state == "Established":
- no_of_peer += 1
- if no_of_peer == total_peer:
- logger.info("BGP is Converged for router %s", router)
- break
- else:
- logger.warning("BGP is not yet Converged for router %s",
- router)
- sleeptime = 2 * retry
- if sleeptime <= BGP_CONVERGENCE_TIMEOUT:
- # Waiting for BGP to converge
- logger.info("Waiting for %s sec for BGP to converge on"
- " router %s...", sleeptime, router)
- sleep(sleeptime)
- else:
- show_bgp_summary = rnode.vtysh_cmd("show bgp summary")
- errormsg = "TIMEOUT!! BGP is not converged in {} " \
- "seconds for router {} \n {}".format(
- BGP_CONVERGENCE_TIMEOUT, router,
- show_bgp_summary)
- return errormsg
+ 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.iteritems():
+ for dest_link in peer_data["dest_link"].keys():
+ data = topo["routers"][bgp_neighbor]["links"]
+ if dest_link in data:
+ neighbor_ip = \
+ data[dest_link][addr_type].split("/")[0]
+ if addr_type == "ipv4":
+ ipv4_data = show_bgp_json["ipv4Unicast"][
+ "peers"]
+ nh_state = ipv4_data[neighbor_ip]["state"]
+ else:
+ ipv6_data = show_bgp_json["ipv6Unicast"][
+ "peers"]
+ nh_state = ipv6_data[neighbor_ip]["state"]
+
+ if nh_state == "Established":
+ no_of_peer += 1
+ if no_of_peer == total_peer:
+ logger.info("BGP is Converged for router %s", router)
+ else:
+ errormsg = "BGP is not converged for router {}".format(
+ router)
+ return errormsg
- logger.info("Exiting API: verify_bgp_confergence()")
+ logger.debug("Exiting API: verify_bgp_convergence()")
return True
@@ -723,7 +704,7 @@ def modify_as_number(tgen, topo, input_dict):
errormsg(str) or True
"""
- logger.info("Entering lib API: modify_as_number()")
+ logger.debug("Entering lib API: modify_as_number()")
try:
new_topo = deepcopy(topo["routers"])
@@ -757,11 +738,12 @@ def modify_as_number(tgen, topo, input_dict):
logger.error(errormsg)
return errormsg
- logger.info("Exiting lib API: modify_as_number()")
+ logger.debug("Exiting lib API: modify_as_number()")
return True
+@retry(attempts=3, wait=2, return_is_str=True)
def verify_as_numbers(tgen, topo, input_dict):
"""
This API is to verify AS numbers for given DUT by running
@@ -791,7 +773,7 @@ def verify_as_numbers(tgen, topo, input_dict):
errormsg(str) or True
"""
- logger.info("Entering lib API: verify_as_numbers()")
+ logger.debug("Entering lib API: verify_as_numbers()")
for router in input_dict.keys():
if router not in tgen.routers():
continue
@@ -800,7 +782,7 @@ def verify_as_numbers(tgen, topo, input_dict):
logger.info("Verifying AS numbers for dut %s:", router)
- show_ip_bgp_neighbor_json = rnode.vtysh_cmd(
+ show_ip_bgp_neighbor_json = run_frr_cmd(rnode,
"show ip bgp neighbor json", isjson=True)
local_as = input_dict[router]["bgp"]["local_as"]
bgp_addr_type = topo["routers"][router]["bgp"]["address_family"]
@@ -846,7 +828,7 @@ def verify_as_numbers(tgen, topo, input_dict):
"neighbor %s, found expected: %s",
router, bgp_neighbor, remote_as)
- logger.info("Exiting lib API: verify_AS_numbers()")
+ logger.debug("Exiting lib API: verify_AS_numbers()")
return True
@@ -873,7 +855,7 @@ def clear_bgp_and_verify(tgen, topo, router):
errormsg(str) or True
"""
- logger.info("Entering lib API: clear_bgp_and_verify()")
+ logger.debug("Entering lib API: clear_bgp_and_verify()")
if router not in tgen.routers():
return False
@@ -883,20 +865,14 @@ def clear_bgp_and_verify(tgen, topo, router):
peer_uptime_before_clear_bgp = {}
# Verifying BGP convergence before bgp clear command
for retry in range(1, 11):
- sleeptime = 2 * retry
- if sleeptime <= BGP_CONVERGENCE_TIMEOUT:
- # Waiting for BGP to converge
- logger.info("Waiting for %s sec for BGP to converge on router"
- " %s...", sleeptime, router)
- sleep(sleeptime)
- else:
- errormsg = "TIMEOUT!! BGP is not converged in {} seconds for" \
- " router {}".format(BGP_CONVERGENCE_TIMEOUT, router)
- return errormsg
+ sleeptime = 3
+ # Waiting for BGP to converge
+ logger.info("Waiting for %s sec for BGP to converge on router"
+ " %s...", sleeptime, router)
+ sleep(sleeptime)
- show_bgp_json = rnode.vtysh_cmd("show bgp summary json",
+ show_bgp_json = run_frr_cmd(rnode, "show bgp summary json",
isjson=True)
- logger.info(show_bgp_json)
# Verifying output dictionary show_bgp_json is empty or not
if not bool(show_bgp_json):
errormsg = "BGP is not running"
@@ -950,33 +926,33 @@ def clear_bgp_and_verify(tgen, topo, router):
" clear", router)
break
else:
- logger.warning("BGP is not yet Converged for router %s "
- "before bgp clear", router)
+ logger.info("BGP is not yet Converged for router %s "
+ "before bgp clear", router)
+ else:
+ errormsg = "TIMEOUT!! BGP is not converged in 30 seconds for" \
+ " router {}".format(router)
+ return errormsg
logger.info(peer_uptime_before_clear_bgp)
# Clearing BGP
logger.info("Clearing BGP neighborship for router %s..", router)
for addr_type in bgp_addr_type.keys():
if addr_type == "ipv4":
- rnode.vtysh_cmd("clear ip bgp *")
+ run_frr_cmd(rnode, "clear ip bgp *")
elif addr_type == "ipv6":
- rnode.vtysh_cmd("clear bgp ipv6 *")
+ run_frr_cmd(rnode, "clear bgp ipv6 *")
peer_uptime_after_clear_bgp = {}
# Verifying BGP convergence after bgp clear command
- for retry in range(1, 11):
- sleeptime = 2 * retry
- if sleeptime <= BGP_CONVERGENCE_TIMEOUT:
- # Waiting for BGP to converge
- logger.info("Waiting for %s sec for BGP to converge on router"
- " %s...", sleeptime, router)
- sleep(sleeptime)
- else:
- errormsg = "TIMEOUT!! BGP is not converged in {} seconds for" \
- " router {}".format(BGP_CONVERGENCE_TIMEOUT, router)
- return errormsg
+ for retry in range(11):
+ sleeptime = 3
+ # Waiting for BGP to converge
+ logger.info("Waiting for %s sec for BGP to converge on router"
+ " %s...", sleeptime, router)
+ sleep(sleeptime)
+
- show_bgp_json = rnode.vtysh_cmd("show bgp summary json",
+ show_bgp_json = run_frr_cmd(rnode, "show bgp summary json",
isjson=True)
# Verifying output dictionary show_bgp_json is empty or not
if not bool(show_bgp_json):
@@ -1028,9 +1004,12 @@ def clear_bgp_and_verify(tgen, topo, router):
router)
break
else:
- logger.warning("BGP is not yet Converged for router %s after"
- " bgp clear", router)
-
+ logger.info("BGP is not yet Converged for router %s after"
+ " bgp clear", router)
+ else:
+ errormsg = "TIMEOUT!! BGP is not converged in 30 seconds for" \
+ " router {}".format(router)
+ return errormsg
logger.info(peer_uptime_after_clear_bgp)
# Comparing peerUptimeEstablishedEpoch dictionaries
if peer_uptime_before_clear_bgp != peer_uptime_after_clear_bgp:
@@ -1041,7 +1020,7 @@ def clear_bgp_and_verify(tgen, topo, router):
" {}".format(router)
return errormsg
- logger.info("Exiting lib API: clear_bgp_and_verify()")
+ logger.debug("Exiting lib API: clear_bgp_and_verify()")
return True
@@ -1077,7 +1056,7 @@ def verify_bgp_timers_and_functionality(tgen, topo, input_dict):
errormsg(str) or True
"""
- logger.info("Entering lib API: verify_bgp_timers_and_functionality()")
+ logger.debug("Entering lib API: verify_bgp_timers_and_functionality()")
sleep(5)
router_list = tgen.routers()
for router in input_dict.keys():
@@ -1090,7 +1069,7 @@ def verify_bgp_timers_and_functionality(tgen, topo, input_dict):
router)
show_ip_bgp_neighbor_json = \
- rnode.vtysh_cmd("show ip bgp neighbor json", isjson=True)
+ run_frr_cmd(rnode, "show ip bgp neighbor json", isjson=True)
bgp_addr_type = input_dict[router]["bgp"]["address_family"]
@@ -1178,7 +1157,7 @@ def verify_bgp_timers_and_functionality(tgen, topo, input_dict):
sleep(keepalivetimer)
sleep(2)
show_bgp_json = \
- rnode.vtysh_cmd("show bgp summary json",
+ run_frr_cmd(rnode, "show bgp summary json",
isjson=True)
if addr_type == "ipv4":
@@ -1192,17 +1171,13 @@ def verify_bgp_timers_and_functionality(tgen, topo, input_dict):
(holddowntimer - keepalivetimer):
if nh_state != "Established":
errormsg = "BGP neighborship has not gone " \
- "down in {} sec for neighbor {}\n" \
- "show_bgp_json: \n {} ".format(
- timer, bgp_neighbor,
- show_bgp_json)
+ "down in {} sec for neighbor {}" \
+ .format(timer, bgp_neighbor)
return errormsg
else:
logger.info("BGP neighborship is intact in %s"
- " sec for neighbor %s \n "
- "show_bgp_json : \n %s",
- timer, bgp_neighbor,
- show_bgp_json)
+ " sec for neighbor %s",
+ timer, bgp_neighbor)
####################
# Shutting down peer interface and verifying that BGP
@@ -1229,7 +1204,7 @@ def verify_bgp_timers_and_functionality(tgen, topo, input_dict):
sleep(keepalivetimer)
sleep(2)
show_bgp_json = \
- rnode.vtysh_cmd("show bgp summary json",
+ run_frr_cmd(rnode, "show bgp summary json",
isjson=True)
if addr_type == "ipv4":
@@ -1242,22 +1217,19 @@ def verify_bgp_timers_and_functionality(tgen, topo, input_dict):
if timer == holddowntimer:
if nh_state == "Established":
errormsg = "BGP neighborship has not gone " \
- "down in {} sec for neighbor {}\n" \
- "show_bgp_json: \n {} ".format(
- timer, bgp_neighbor,
- show_bgp_json)
+ "down in {} sec for neighbor {}" \
+ .format(timer, bgp_neighbor)
return errormsg
else:
logger.info("BGP neighborship has gone down in"
- " %s sec for neighbor %s \n"
- "show_bgp_json : \n %s",
- timer, bgp_neighbor,
- show_bgp_json)
+ " %s sec for neighbor %s",
+ timer, bgp_neighbor)
- logger.info("Exiting lib API: verify_bgp_timers_and_functionality()")
+ logger.debug("Exiting lib API: verify_bgp_timers_and_functionality()")
return True
+@retry(attempts=3, wait=2, return_is_str=True)
def verify_best_path_as_per_bgp_attribute(tgen, addr_type, router, input_dict,
attribute):
"""
@@ -1319,7 +1291,7 @@ def verify_best_path_as_per_bgp_attribute(tgen, addr_type, router, input_dict,
sleep(2)
logger.info("Verifying router %s RIB for best path:", router)
- sh_ip_bgp_json = rnode.vtysh_cmd(command, isjson=True)
+ sh_ip_bgp_json = run_frr_cmd(rnode, command, isjson=True)
for route_val in input_dict.values():
net_data = route_val["bgp"]["address_family"]["ipv4"]["unicast"]
@@ -1380,7 +1352,7 @@ def verify_best_path_as_per_bgp_attribute(tgen, addr_type, router, input_dict,
else:
command = "show ipv6 route json"
- rib_routes_json = rnode.vtysh_cmd(command, isjson=True)
+ rib_routes_json = run_frr_cmd(rnode, command, isjson=True)
# Verifying output dictionary rib_routes_json is not empty
if not bool(rib_routes_json):
@@ -1417,6 +1389,7 @@ def verify_best_path_as_per_bgp_attribute(tgen, addr_type, router, input_dict,
return True
+@retry(attempts=3, wait=2, return_is_str=True)
def verify_best_path_as_per_admin_distance(tgen, addr_type, router, input_dict,
attribute):
"""
@@ -1451,7 +1424,7 @@ def verify_best_path_as_per_admin_distance(tgen, addr_type, router, input_dict,
errormsg(str) or True
"""
- logger.info("Entering lib API: verify_best_path_as_per_admin_distance()")
+ logger.debug("Entering lib API: verify_best_path_as_per_admin_distance()")
router_list = tgen.routers()
if router not in router_list:
return False
@@ -1490,7 +1463,7 @@ def verify_best_path_as_per_admin_distance(tgen, addr_type, router, input_dict,
compare = "LOWEST"
# Show ip route
- rib_routes_json = rnode.vtysh_cmd(command, isjson=True)
+ rib_routes_json = run_frr_cmd(rnode, command, isjson=True)
# Verifying output dictionary rib_routes_json is not empty
if not bool(rib_routes_json):
diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py
index 75880cfd28..f2d33f94ae 100644
--- a/tests/topotests/lib/common_config.py
+++ b/tests/topotests/lib/common_config.py
@@ -23,24 +23,32 @@ from datetime import datetime
from time import sleep
from subprocess import call
from subprocess import STDOUT as SUB_STDOUT
+from subprocess import PIPE as SUB_PIPE
+from subprocess import Popen
+from functools import wraps
+from re import search as re_search
+
import StringIO
import os
import ConfigParser
import traceback
import socket
import ipaddr
+import re
from lib import topotest
from functools import partial
from lib.topolog import logger, logger_config
from lib.topogen import TopoRouter
+from lib.topotest import interface_set_status
FRRCFG_FILE = "frr_json.conf"
FRRCFG_BKUP_FILE = "frr_json_initial.conf"
ERROR_LIST = ["Malformed", "Failure", "Unknown"]
+ROUTER_LIST = []
####
CD = os.path.dirname(os.path.realpath(__file__))
@@ -142,6 +150,35 @@ class InvalidCLIError(Exception):
pass
+def run_frr_cmd(rnode, cmd, isjson=False):
+ """
+ Execute frr show commands in priviledged mode
+
+ * `rnode`: router node on which commands needs to executed
+ * `cmd`: Command to be executed on frr
+ * `isjson`: If command is to get json data or not
+
+ :return str:
+ """
+
+ if cmd:
+ ret_data = rnode.vtysh_cmd(cmd, isjson=isjson)
+
+ if True:
+ if isjson:
+ logger.debug(ret_data)
+ print_data = rnode.vtysh_cmd(cmd.rstrip("json"), isjson=False)
+ else:
+ print_data = ret_data
+
+ logger.info('Output for command [ %s] on router %s:\n%s',
+ cmd.rstrip("json"), rnode.name, print_data)
+ return ret_data
+
+ else:
+ raise InvalidCLIError('No actual cmd passed')
+
+
def create_common_configuration(tgen, router, data, config_type=None,
build=False):
"""
@@ -186,6 +223,7 @@ def create_common_configuration(tgen, router, data, config_type=None,
frr_cfg_fd.write(config_map[config_type])
for line in data:
frr_cfg_fd.write("{} \n".format(str(line)))
+ frr_cfg_fd.write("\n")
except IOError as err:
logger.error("Unable to open FRR Config File. error(%s): %s" %
@@ -215,10 +253,13 @@ def reset_config_on_routers(tgen, routerName=None):
logger.debug("Entering API: reset_config_on_routers")
router_list = tgen.routers()
- for rname, router in router_list.iteritems():
+ for rname in ROUTER_LIST:
if routerName and routerName != rname:
continue
+ router = router_list[rname]
+ logger.info("Configuring router %s to initial test configuration",
+ rname)
cfg = router.run("vtysh -c 'show running'")
fname = "{}/{}/frr.sav".format(TMPDIR, rname)
dname = "{}/{}/delta.conf".format(TMPDIR, rname)
@@ -235,16 +276,35 @@ def reset_config_on_routers(tgen, routerName=None):
f.close()
- command = "/usr/lib/frr/frr-reload.py --input {}/{}/frr.sav" \
- " --test {}/{}/frr_json_initial.conf > {}". \
- format(TMPDIR, rname, TMPDIR, rname, dname)
- result = call(command, shell=True, stderr=SUB_STDOUT)
+ 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(run_cfg_file, init_cfg_file, dname)
+ result = call(command, shell=True, stderr=SUB_STDOUT,
+ stdout=SUB_PIPE)
# Assert if command fail
if result > 0:
- errormsg = ("Command:{} is failed due to non-zero exit"
- " code".format(command))
- return errormsg
+ logger.error("Delta file creation failed. Command executed %s",
+ command)
+ with open(run_cfg_file, 'r') as fd:
+ logger.info('Running configuration saved in %s is:\n%s',
+ run_cfg_file, fd.read())
+ with open(init_cfg_file, 'r') as fd:
+ logger.info('Test configuration saved in %s is:\n%s',
+ init_cfg_file, fd.read())
+
+ err_cmd = ['/usr/bin/vtysh', '-m', '-f', run_cfg_file]
+ result = Popen(err_cmd, stdout=SUB_PIPE, stderr=SUB_PIPE)
+ output = result.communicate()
+ for out_data in output:
+ temp_data = out_data.decode('utf-8').lower()
+ for out_err in ERROR_LIST:
+ if out_err.lower() in temp_data:
+ logger.error("Found errors while validating data in"
+ " %s", run_cfg_file)
+ raise InvalidCLIError(out_data)
+ raise InvalidCLIError("Unknown error in %s", output)
f = open(dname, "r")
delta = StringIO.StringIO()
@@ -264,7 +324,7 @@ def reset_config_on_routers(tgen, routerName=None):
delta.write("end\n")
output = router.vtysh_multicmd(delta.getvalue(),
pretty_output=False)
- logger.info("New configuration for router {}:".format(rname))
+
delta.close()
delta = StringIO.StringIO()
cfg = router.run("vtysh -c 'show running'")
@@ -276,6 +336,8 @@ def reset_config_on_routers(tgen, routerName=None):
# Router current configuration to log file or console if
# "show_router_config" is defined in "pytest.ini"
if show_router_config:
+ logger.info("Configuration on router {} after config reset:".
+ format(rname))
logger.info(delta.getvalue())
delta.close()
@@ -297,34 +359,39 @@ def load_config_to_router(tgen, routerName, save_bkup=False):
logger.debug("Entering API: load_config_to_router")
router_list = tgen.routers()
- for rname, router in router_list.iteritems():
- if rname == routerName:
- try:
- frr_cfg_file = "{}/{}/{}".format(TMPDIR, rname, FRRCFG_FILE)
- frr_cfg_bkup = "{}/{}/{}".format(TMPDIR, rname,
- FRRCFG_BKUP_FILE)
- with open(frr_cfg_file, "r") as cfg:
- data = cfg.read()
- if save_bkup:
- with open(frr_cfg_bkup, "w") as bkup:
- bkup.write(data)
-
- output = router.vtysh_multicmd(data, pretty_output=False)
- for out_err in ERROR_LIST:
- if out_err.lower() in output.lower():
- raise InvalidCLIError("%s" % output)
- except IOError as err:
- errormsg = ("Unable to open config File. error(%s):"
- " %s", (err.errno, err.strerror))
- return errormsg
+ for rname in ROUTER_LIST:
+ if routerName and routerName != rname:
+ continue
- logger.info("New configuration for router {}:".format(rname))
- new_config = router.run("vtysh -c 'show running'")
+ router = router_list[rname]
+ try:
+ frr_cfg_file = "{}/{}/{}".format(TMPDIR, rname, FRRCFG_FILE)
+ frr_cfg_bkup = "{}/{}/{}".format(TMPDIR, rname,
+ FRRCFG_BKUP_FILE)
+ with open(frr_cfg_file, "r+") as cfg:
+ data = cfg.read()
+ logger.info("Applying following configuration on router"
+ " {}:\n{}".format(rname, data))
+ if save_bkup:
+ with open(frr_cfg_bkup, "w") as bkup:
+ bkup.write(data)
+
+ output = router.vtysh_multicmd(data, pretty_output=False)
+ for out_err in ERROR_LIST:
+ if out_err.lower() in output.lower():
+ raise InvalidCLIError("%s" % output)
+
+ cfg.truncate(0)
+ except IOError as err:
+ errormsg = ("Unable to open config File. error(%s):"
+ " %s", (err.errno, err.strerror))
+ return errormsg
- # Router current configuration to log file or console if
- # "show_router_config" is defined in "pytest.ini"
- if show_router_config:
- logger.info(new_config)
+ # Router current configuration to log file or console if
+ # "show_router_config" is defined in "pytest.ini"
+ if show_router_config:
+ new_config = router.run("vtysh -c 'show running'")
+ logger.info(new_config)
logger.debug("Exting API: load_config_to_router")
return True
@@ -337,21 +404,25 @@ def start_topology(tgen):
* `tgen` : topogen object
"""
- global TMPDIR
+ global TMPDIR, ROUTER_LIST
# Starting topology
tgen.start_topology()
# Starting deamons
+
router_list = tgen.routers()
+ ROUTER_LIST = sorted(router_list.keys(),
+ key=lambda x: int(re_search('\d+', x).group(0)))
TMPDIR = os.path.join(LOGDIR, tgen.modname)
- for rname, router in router_list.iteritems():
+ router_list = tgen.routers()
+ for rname in ROUTER_LIST:
+ router = router_list[rname]
try:
os.chdir(TMPDIR)
- # Creating rouer named dir and empty zebra.conf bgpd.conf files
+ # Creating router named dir and empty zebra.conf bgpd.conf files
# inside the current directory
-
if os.path.isdir('{}'.format(rname)):
os.system("rm -rf {}".format(rname))
os.mkdir('{}'.format(rname))
@@ -371,13 +442,11 @@ def start_topology(tgen):
router.load_config(
TopoRouter.RD_ZEBRA,
'{}/{}/zebra.conf'.format(TMPDIR, rname)
- # os.path.join(tmpdir, '{}/zebra.conf'.format(rname))
)
# Loading empty bgpd.conf file to router, to start the bgp deamon
router.load_config(
TopoRouter.RD_BGP,
'{}/{}/bgpd.conf'.format(TMPDIR, rname)
- # os.path.join(tmpdir, '{}/bgpd.conf'.format(rname))
)
# Starting routers
@@ -446,27 +515,31 @@ def validate_ip_address(ip_address):
" address" % ip_address)
-def check_address_types(addr_type):
+def check_address_types(addr_type=None):
"""
Checks environment variable set and compares with the current address type
"""
- global ADDRESS_TYPES
- if ADDRESS_TYPES is None:
- ADDRESS_TYPES = "dual"
-
- if ADDRESS_TYPES == "dual":
- ADDRESS_TYPES = ["ipv4", "ipv6"]
- elif ADDRESS_TYPES == "ipv4":
- ADDRESS_TYPES = ["ipv4"]
- elif ADDRESS_TYPES == "ipv6":
- ADDRESS_TYPES = ["ipv6"]
-
- if addr_type not in ADDRESS_TYPES:
+
+ addr_types_env = os.environ.get("ADDRESS_TYPES")
+ if not addr_types_env:
+ addr_types_env = "dual"
+
+ if addr_types_env == "dual":
+ addr_types = ["ipv4", "ipv6"]
+ elif addr_types_env == "ipv4":
+ addr_types = ["ipv4"]
+ elif addr_types_env == "ipv6":
+ addr_types = ["ipv6"]
+
+ if addr_type is None:
+ return addr_types
+
+ if addr_type not in addr_types:
logger.error("{} not in supported/configured address types {}".
- format(addr_type, ADDRESS_TYPES))
+ format(addr_type, addr_types))
return False
- return ADDRESS_TYPES
+ return True
def generate_ips(network, no_of_ips):
@@ -548,7 +621,7 @@ def write_test_header(tc_name):
""" Display message at beginning of test case"""
count = 20
logger.info("*"*(len(tc_name)+count))
- logger.info("START -> Testcase : %s", tc_name)
+ logger.info("START -> Testcase : %s" % tc_name)
logger.info("*"*(len(tc_name)+count))
@@ -556,10 +629,169 @@ def write_test_footer(tc_name):
""" Display message at end of test case"""
count = 21
logger.info("="*(len(tc_name)+count))
- logger.info("PASSED -> Testcase : %s", tc_name)
+ logger.info("Testcase : %s -> PASSED", tc_name)
logger.info("="*(len(tc_name)+count))
+def interface_status(tgen, topo, input_dict):
+ """
+ Delete ip route maps from device
+
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `input_dict` : for which router, route map has to be deleted
+
+ Usage
+ -----
+ input_dict = {
+ "r3": {
+ "interface_list": ['eth1-r1-r2', 'eth2-r1-r3'],
+ "status": "down"
+ }
+ }
+ Returns
+ -------
+ errormsg(str) or True
+ """
+ logger.debug("Entering lib API: interface_status()")
+
+ try:
+ global frr_cfg
+ for router in input_dict.keys():
+
+ interface_list = input_dict[router]['interface_list']
+ status = input_dict[router].setdefault('status', 'up')
+ for intf in interface_list:
+ rnode = tgen.routers()[router]
+ interface_set_status(rnode, intf, status)
+
+ # Load config to router
+ load_config_to_router(tgen, router)
+
+ except Exception as e:
+ # handle any exception
+ logger.error("Error %s occured. Arguments %s.", e.message, e.args)
+
+ # Traceback
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
+
+ logger.debug("Exiting lib API: interface_status()")
+ return True
+
+
+def retry(attempts=3, wait=2, return_is_str=True, initial_wait=0):
+ """
+ Retries function execution, if return is an errormsg or exception
+
+ * `attempts`: Number of attempts to make
+ * `wait`: Number of seconds to wait between each attempt
+ * `return_is_str`: Return val is an errormsg in case of failure
+ * `initial_wait`: Sleeps for this much seconds before executing function
+
+ """
+
+ def _retry(func):
+
+ @wraps(func)
+ def func_retry(*args, **kwargs):
+ _wait = kwargs.pop('wait', wait)
+ _attempts = kwargs.pop('attempts', attempts)
+ _attempts = int(_attempts)
+ if _attempts < 0:
+ raise ValueError("attempts must be 0 or greater")
+
+ if initial_wait > 0:
+ logger.info("Waiting for [%s]s as initial delay", initial_wait)
+ sleep(initial_wait)
+
+ _return_is_str = kwargs.pop('return_is_str', return_is_str)
+ for i in range(1, _attempts + 1):
+ try:
+ _expected = kwargs.setdefault('expected', True)
+ kwargs.pop('expected')
+ ret = func(*args, **kwargs)
+ logger.debug("Function returned %s" % ret)
+ if return_is_str and isinstance(ret, bool):
+ return ret
+ elif return_is_str and _expected is False:
+ return ret
+
+ if _attempts == i:
+ return ret
+ except Exception as err:
+ if _attempts == i:
+ logger.info("Max number of attempts (%r) reached",
+ _attempts)
+ raise
+ else:
+ logger.info("Function returned %s", err)
+ if i < _attempts:
+ logger.info("Retry [#%r] after sleeping for %ss"
+ % (i, _wait))
+ sleep(_wait)
+ func_retry._original = func
+ return func_retry
+ return _retry
+
+
+def disable_v6_link_local(tgen, router, intf_name=None):
+ """
+ Disables ipv6 link local addresses for a particular interface or
+ all interfaces
+
+ * `tgen`: tgen onject
+ * `router` : router for which hightest interface should be
+ calculated
+ * `intf_name` : Interface name for which v6 link local needs to
+ be disabled
+ """
+
+ router_list = tgen.routers()
+ for rname, rnode in router_list.iteritems():
+ if rname != router:
+ continue
+
+ linklocal = []
+
+ ifaces = router_list[router].run('ip -6 address')
+
+ # 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('[0-9]+: ([^:]+)[@if0-9:]+ <', line)
+ if m:
+ interface = m.group(1).split("@")[0]
+ ll_per_if_count = 0
+
+ # Interface ip
+ m = re.search('inet6 (fe80::[0-9a-f]+:[0-9a-f]+:[0-9a-f]+'
+ ':[0-9a-f]+[/0-9]*) scope link', line)
+ if m:
+ local = m.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 len(linklocal[0]) > 1:
+ link_local_dict = {item[0]: item[1] for item in linklocal}
+
+ for lname, laddr in link_local_dict.items():
+
+ if intf_name is not None and lname != intf_name:
+ continue
+
+ cmd = "ip addr del {} dev {}".format(laddr, lname)
+ router_list[router].run(cmd)
+
+
#############################################
# These APIs, will used by testcase
#############################################
@@ -589,19 +821,22 @@ def create_interfaces_cfg(tgen, topo, build=False):
interface_name = destRouterLink
else:
interface_name = data["interface"]
- interface_data.append("interface {}\n".format(
+ if "ipv6" in data:
+ disable_v6_link_local(tgen, c_router, interface_name)
+ interface_data.append("interface {}".format(
str(interface_name)
))
if "ipv4" in data:
intf_addr = c_data["links"][destRouterLink]["ipv4"]
- interface_data.append("ip address {}\n".format(
+ interface_data.append("ip address {}".format(
intf_addr
))
if "ipv6" in data:
intf_addr = c_data["links"][destRouterLink]["ipv6"]
- interface_data.append("ipv6 address {}\n".format(
+ interface_data.append("ipv6 address {}".format(
intf_addr
))
+
result = create_common_configuration(tgen, c_router,
interface_data,
"interface_config",
@@ -662,7 +897,7 @@ def create_static_routes(tgen, input_dict, build=False):
for router in input_dict.keys():
if "static_routes" not in input_dict[router]:
errormsg = "static_routes not present in input_dict"
- logger.info(errormsg)
+ logger.debug(errormsg)
continue
static_routes_list = []
@@ -768,7 +1003,7 @@ def create_prefix_lists(tgen, input_dict, build=False):
for router in input_dict.keys():
if "prefix_lists" not in input_dict[router]:
errormsg = "prefix_lists not present in input_dict"
- logger.info(errormsg)
+ logger.debug(errormsg)
continue
config_data = []
@@ -922,7 +1157,7 @@ def create_route_maps(tgen, input_dict, build=False):
for router in input_dict.keys():
if "route_maps" not in input_dict[router]:
errormsg = "route_maps not present in input_dict"
- logger.info(errormsg)
+ logger.debug(errormsg)
continue
rmap_data = []
for rmap_name, rmap_value in \
@@ -1014,7 +1249,7 @@ def create_route_maps(tgen, input_dict, build=False):
# Weight
if weight:
- rmap_data.append("set weight {} \n".format(
+ rmap_data.append("set weight {}".format(
weight))
# Adding MATCH and SET sequence to RMAP if defined
@@ -1092,7 +1327,8 @@ def create_route_maps(tgen, input_dict, build=False):
#############################################
# Verification APIs
#############################################
-def _verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None):
+@retry(attempts=10, return_is_str=True, initial_wait=2)
+def verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None):
"""
Data will be read from input_dict or input JSON file, API will generate
same prefixes, which were redistributed by either create_static_routes() or
@@ -1140,7 +1376,7 @@ def _verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None):
errormsg(str) or True
"""
- logger.info("Entering lib API: verify_rib()")
+ logger.debug("Entering lib API: verify_rib()")
router_list = tgen.routers()
for routerInput in input_dict.keys():
@@ -1160,9 +1396,8 @@ def _verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None):
else:
command = "show ipv6 route json"
- sleep(10)
logger.info("Checking router %s RIB:", router)
- rib_routes_json = rnode.vtysh_cmd(command, isjson=True)
+ rib_routes_json = run_frr_cmd(rnode, command, isjson=True)
# Verifying output dictionary rib_routes_json is not empty
if bool(rib_routes_json) is False:
@@ -1181,7 +1416,7 @@ def _verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None):
if "no_of_ip" in static_route:
no_of_ip = static_route["no_of_ip"]
else:
- no_of_ip = 0
+ no_of_ip = 1
# Generating IPs for verification
ip_list = generate_ips(network, no_of_ip)
@@ -1199,9 +1434,9 @@ def _verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None):
found_hops = [rib_r["ip"] for rib_r in
rib_routes_json[st_rt][0][
"nexthops"]]
- for nh in next_hop:
+ for nh in found_hops:
nh_found = False
- if nh and nh in found_hops:
+ if nh and nh in next_hop:
nh_found = True
else:
errormsg = ("Nexthop {} is Missing for {}"
@@ -1257,30 +1492,10 @@ def _verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None):
logger.info("Verified routes in router %s RIB, found routes"
" are: %s", dut, found_routes)
- logger.info("Exiting lib API: verify_rib()")
+ logger.debug("Exiting lib API: verify_rib()")
return True
-def verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None, expected=True):
- """
- Wrapper function for `_verify_rib` that tries multiple time to get results.
-
- When the expected result is `False` we actually should expect for an string instead.
- """
-
- # Use currying to hide the parameters and create a test function.
- test_func = partial(_verify_rib, tgen, addr_type, dut, input_dict, next_hop, protocol)
-
- # Call the test function and expect it to return True, otherwise try it again.
- if expected is True:
- _, result = topotest.run_and_expect(test_func, True, count=20, wait=6)
- else:
- _, result = topotest.run_and_expect_type(test_func, str, count=20, wait=6)
-
- # Return as normal.
- return result
-
-
def verify_admin_distance_for_static_routes(tgen, input_dict):
"""
API to verify admin distance for static routes as defined in input_dict/
@@ -1311,7 +1526,7 @@ def verify_admin_distance_for_static_routes(tgen, input_dict):
errormsg(str) or True
"""
- logger.info("Entering lib API: verify_admin_distance_for_static_routes()")
+ logger.debug("Entering lib API: verify_admin_distance_for_static_routes()")
for router in input_dict.keys():
if router not in tgen.routers():
@@ -1326,7 +1541,7 @@ def verify_admin_distance_for_static_routes(tgen, input_dict):
command = "show ip route json"
else:
command = "show ipv6 route json"
- show_ip_route_json = rnode.vtysh_cmd(command, isjson=True)
+ show_ip_route_json = run_frr_cmd(rnode, command, isjson=True)
logger.info("Verifying admin distance for static route %s"
" under dut %s:", static_route, router)
@@ -1356,7 +1571,7 @@ def verify_admin_distance_for_static_routes(tgen, input_dict):
format(network, router))
return errormsg
- logger.info("Exiting lib API: verify_admin_distance_for_static_routes()")
+ logger.debug("Exiting lib API: verify_admin_distance_for_static_routes()")
return True
@@ -1384,7 +1599,7 @@ def verify_prefix_lists(tgen, input_dict):
errormsg(str) or True
"""
- logger.info("Entering lib API: verify_prefix_lists()")
+ logger.debug("Entering lib API: verify_prefix_lists()")
for router in input_dict.keys():
if router not in tgen.routers():
@@ -1393,7 +1608,7 @@ def verify_prefix_lists(tgen, input_dict):
rnode = tgen.routers()[router]
# Show ip prefix list
- show_prefix_list = rnode.vtysh_cmd("show ip prefix-list")
+ show_prefix_list = run_frr_cmd(rnode, "show ip prefix-list")
# Verify Prefix list is deleted
prefix_lists_addr = input_dict[router]["prefix_lists"]
@@ -1403,12 +1618,12 @@ def verify_prefix_lists(tgen, input_dict):
for prefix_list in prefix_lists_addr[addr_type].keys():
if prefix_list in show_prefix_list:
- errormsg = ("Prefix list {} is not deleted from router"
+ errormsg = ("Prefix list {} is/are present in the router"
" {}".format(prefix_list, router))
return errormsg
- logger.info("Prefix list %s is/are deleted successfully"
+ logger.info("Prefix list %s is/are not present in the router"
" from router %s", prefix_list, router)
- logger.info("Exiting lib API: verify_prefix_lissts()")
+ logger.debug("Exiting lib API: verify_prefix_lissts()")
return True
diff --git a/tests/topotests/lib/topojson.py b/tests/topotests/lib/topojson.py
index 4130451d2e..7a00fe4c50 100644
--- a/tests/topotests/lib/topojson.py
+++ b/tests/topotests/lib/topojson.py
@@ -20,6 +20,7 @@
from collections import OrderedDict
from json import dumps as json_dumps
+from re import search as re_search
import ipaddr
import pytest
@@ -38,6 +39,9 @@ from lib.common_config import (
from lib.bgp import create_router_bgp
+ROUTER_LIST = []
+
+
def build_topo_from_json(tgen, topo):
"""
Reads configuration from JSON file. Adds routers, creates interface
@@ -48,13 +52,15 @@ def build_topo_from_json(tgen, topo):
* `topo`: json file data
"""
- listRouters = []
- for routerN in sorted(topo['routers'].iteritems()):
- logger.info('Topo: Add router {}'.format(routerN[0]))
- tgen.add_router(routerN[0])
- listRouters.append(routerN[0])
+ ROUTER_LIST = sorted(topo['routers'].keys(),
+ key=lambda x: int(re_search('\d+', x).group(0)))
+
+ listRouters = ROUTER_LIST[:]
+ for routerN in ROUTER_LIST:
+ logger.info('Topo: Add router {}'.format(routerN))
+ tgen.add_router(routerN)
+ listRouters.append(routerN)
- listRouters.sort()
if 'ipv4base' in topo:
ipv4Next = ipaddr.IPv4Address(topo['link_ip_start']['ipv4'])
ipv4Step = 2 ** (32 - topo['link_ip_start']['v4mask'])
@@ -78,7 +84,7 @@ def build_topo_from_json(tgen, topo):
elif 'link' in x:
return int(x.split('-link')[1])
else:
- return int(x.split('r')[1])
+ return int(re_search('\d+', x).group(0))
for destRouterLink, data in sorted(topo['routers'][curRouter]['links']. \
iteritems(),
key=lambda x: link_sort(x[0])):
@@ -179,12 +185,13 @@ def build_config_from_json(tgen, topo, save_bkup=True):
data = topo["routers"]
for func_type in func_dict.keys():
- logger.info('Building configuration for {}'.format(func_type))
+ logger.info('Checking for {} configuration in input data'.format(
+ func_type))
func_dict.get(func_type)(tgen, data, build=True)
for router in sorted(topo['routers'].keys()):
- logger.info('Configuring router {}...'.format(router))
+ logger.debug('Configuring router {}...'.format(router))
result = load_config_to_router(tgen, router, save_bkup)
if not result:
diff --git a/tests/topotests/pytest.ini b/tests/topotests/pytest.ini
index 7ea38491d8..ade5bfd501 100644
--- a/tests/topotests/pytest.ini
+++ b/tests/topotests/pytest.ini
@@ -1,6 +1,6 @@
# Skip pytests example directory
[pytest]
-norecursedirs = .git example-test lib docker
+norecursedirs = .git example-test example-topojson-test lib docker
[topogen]
# Default configuration values
@@ -15,7 +15,7 @@ norecursedirs = .git example-test lib docker
# Display router current configuration during test execution,
# by default configuration will not be shown
-show_router_config = True
+# show_router_config = True
# Default daemons binaries path.
#frrdir = /usr/lib/frr