diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo1.json b/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo1.json new file mode 100644 index 0000000000..b1d7d09db8 --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo1.json @@ -0,0 +1,563 @@ +{ + "address_types": ["ipv4","ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "ISR"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + { + "name": "ISR", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["11.11.11.1/32", "11.11.11.11/32"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["11:11::1/128", "11:11::11/128"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["10.10.10.10/32", "10.10.10.100/32"], + "next_hop":"Null0" + }, + { + "network": ["10:10::10/128", "10:10::100/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r2": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "ISR"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + { + "name": "ISR", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["22.22.22.2/32", "22.22.22.22/32"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["22:22::2/128", "22:22::22/128"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["20.20.20.20/32", "20.20.20.200/32"], + "next_hop":"Null0" + }, + { + "network": ["20:20::20/128", "20:20::200/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r3": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": + [ + { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["30.30.30.3/32", "30.30.30.30/32"], + "next_hop":"Null0" + }, + { + "network": ["30:30::3/128", "30:30::30/128"], + "next_hop":"Null0" + }, + { + "network": ["50.50.50.5/32", "50.50.50.50/32"], + "next_hop":"Null0" + }, + { + "network": ["50:50::5/128", "50:50::50/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r4": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": + [ + { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["40.40.40.4/32", "40.40.40.40/32"], + "next_hop":"Null0" + }, + { + "network": ["40:40::4/128", "40:40::40/128"], + "next_hop":"Null0" + }, + { + "network": ["50.50.50.5/32", "50.50.50.50/32"], + "next_hop":"Null0" + }, + { + "network": ["50:50::5/128", "50:50::50/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + } + } +} diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo2.json b/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo2.json new file mode 100644 index 0000000000..b1d7d09db8 --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo2.json @@ -0,0 +1,563 @@ +{ + "address_types": ["ipv4","ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "ISR"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + { + "name": "ISR", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["11.11.11.1/32", "11.11.11.11/32"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["11:11::1/128", "11:11::11/128"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["10.10.10.10/32", "10.10.10.100/32"], + "next_hop":"Null0" + }, + { + "network": ["10:10::10/128", "10:10::100/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r2": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "ISR"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + { + "name": "ISR", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["22.22.22.2/32", "22.22.22.22/32"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["22:22::2/128", "22:22::22/128"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["20.20.20.20/32", "20.20.20.200/32"], + "next_hop":"Null0" + }, + { + "network": ["20:20::20/128", "20:20::200/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r3": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": + [ + { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["30.30.30.3/32", "30.30.30.30/32"], + "next_hop":"Null0" + }, + { + "network": ["30:30::3/128", "30:30::30/128"], + "next_hop":"Null0" + }, + { + "network": ["50.50.50.5/32", "50.50.50.50/32"], + "next_hop":"Null0" + }, + { + "network": ["50:50::5/128", "50:50::50/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r4": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": + [ + { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["40.40.40.4/32", "40.40.40.40/32"], + "next_hop":"Null0" + }, + { + "network": ["40:40::4/128", "40:40::40/128"], + "next_hop":"Null0" + }, + { + "network": ["50.50.50.5/32", "50.50.50.50/32"], + "next_hop":"Null0" + }, + { + "network": ["50:50::5/128", "50:50::50/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + } + } +} diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py new file mode 100755 index 0000000000..1947548b3e --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py @@ -0,0 +1,1848 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2020 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 BGP Multi-VRF Dynamic Route Leaking: + +1. Verify that dynamically imported routes are further advertised + to iBGP peers(peer in cluster). +2. Verify matching a prefix based on community attribute and + importing it by stripping off this value +3. Verify the route-map operation along with dynamic import command. +4. Verifying the JSON outputs for all supported commands +""" + +import os +import sys +import json +import time +import pytest +import platform + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) +sys.path.append(os.path.join(CWD, '../lib/')) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, write_test_header, check_address_types, + write_test_footer, reset_config_on_routers, + verify_rib, step, create_route_maps, + shutdown_bringup_interface, create_static_routes, + create_prefix_lists, create_bgp_community_lists, + create_interface_in_kernel, + check_router_status, verify_cli_json, + get_frr_ipv6_linklocal, verify_fib_routes +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, create_router_bgp, + clear_bgp, verify_bgp_community, verify_bgp_rib +) +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/bgp_vrf_dynamic_route_leak_topo1.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 +NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"} +NETWORK1_2 = {"ipv4": "11.11.11.11/32", "ipv6": "11:11::11/128"} +NETWORK1_3 = {"ipv4": "10.10.10.10/32", "ipv6": "10:10::10/128"} +NETWORK1_4 = {"ipv4": "10.10.10.100/32", "ipv6": "10:10::100/128"} + +NETWORK2_1 = {"ipv4": "22.22.22.2/32", "ipv6": "22:22::2/128"} +NETWORK2_2 = {"ipv4": "22.22.22.22/32", "ipv6": "22:22::22/128"} +NETWORK2_3 = {"ipv4": "20.20.20.20/32", "ipv6": "20:20::20/128"} +NETWORK2_4 = {"ipv4": "20.20.20.200/32", "ipv6": "20:20::200/128"} + +NETWORK3_1 = {"ipv4": "30.30.30.3/32", "ipv6": "30:30::3/128"} +NETWORK3_2 = {"ipv4": "30.30.30.30/32", "ipv6": "30:30::30/128"} +NETWORK3_3 = {"ipv4": "50.50.50.5/32", "ipv6": "50:50::5/128"} +NETWORK3_4 = {"ipv4": "50.50.50.50/32", "ipv6": "50:50::50/128"} + +NETWORK4_1 = {"ipv4": "40.40.40.4/32", "ipv6": "40:40::4/128"} +NETWORK4_2 = {"ipv4": "40.40.40.40/32", "ipv6": "40:40::40/128"} +NETWORK4_3 = {"ipv4": "50.50.50.5/32", "ipv6": "50:50::5/128"} +NETWORK4_4 = {"ipv4": "50.50.50.50/32", "ipv6": "50:50::50/128"} + +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} +LOOPBACK_1 = {"ipv4": "10.0.0.7/24", "ipv6": "fd00:0:0:1::7/64", + "ipv4_mask": "255.255.255.0", "ipv6_mask": None} +LOOPBACK_2 = {"ipv4": "10.0.0.16/24", "ipv6": "fd00:0:0:3::5/64", + "ipv4_mask": "255.255.255.0", "ipv6_mask": None} +PREFERRED_NEXT_HOP = "global" + + +class CreateTopo(Topo): + """ + Test BasicTopo - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Run these tests for kernel version 4.19 or above + if version_cmp(platform.release(), '4.19') < 0: + error_msg = ('BGP vrf dynamic route leak tests will not run ' + '(have kernel "{}", but it requires >= 4.19)'.\ + format(platform.release())) + pytest.skip(error_msg) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + 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) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info("Testsuite end time: {}". + format(time.asctime(time.localtime(time.time())))) + logger.info("=" * 40) + +##################################################### +# +# Local APIs +# +##################################################### + +def disable_route_map_to_prefer_global_next_hop(tgen, topo): + """ + This API is to remove prefer global route-map applied on neighbors + + Parameter: + ---------- + * `tgen` : Topogen object + * `topo` : Input JSON data + + Returns: + -------- + True/errormsg + + """ + + logger.info("Remove prefer-global rmap applied on neighbors") + input_dict = { + "r1": { + "bgp": + [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + } + ] + }, + "r2": { + "bgp": + [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + } + ] + }, + "r3": { + "bgp": + [ + { + "local_as": "300", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + }, + { + "local_as": "300", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + } + ] + }, + "r4": { + "bgp": + [ + { + "local_as": "400", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + }, + { + "local_as": "400", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + return True + + +##################################################### +# +# Testcases +# +##################################################### + +def test_dynamic_imported_routes_advertised_to_iBGP_peer_p0(request): + """ + TC5_FUNC_5: + 1.5.5. Verify that dynamically imported routes are further advertised + to iBGP peers(peer in cluster). + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + for addr_type in ADDR_TYPES: + + step("Redistribute configured static routes into BGP process" + " on R1 and R3/R4") + + input_dict_1={} + DUT = ["r1", "r3", "r4"] + VRFS = ["default", "default", "default"] + AS_NUM = [100, 300, 400] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [{ + "redist_type": "static" + }] + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify that R1 receives BGP routes from R3 and R4 in " + "vrf default.") + + input_routes_r3 = { + "r3": { + "static_routes": [{ + "network": [ + NETWORK3_1[addr_type], \ + NETWORK3_2[addr_type], \ + NETWORK3_3[addr_type], \ + NETWORK3_4[addr_type] + ] + }] + } + } + + input_routes_r4 = { + "r4": { + "static_routes": [{ + "network": [ + NETWORK4_1[addr_type], \ + NETWORK4_2[addr_type], \ + NETWORK4_3[addr_type], \ + NETWORK4_4[addr_type] + ] + }] + } + } + + DUT = ["r1", "r2"] + INPUT_DICT = [input_routes_r3, input_routes_r4] + + for dut, routes in zip(DUT, INPUT_DICT): + result = verify_bgp_rib(tgen, addr_type, dut, routes) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + result = verify_fib_routes(tgen, addr_type, dut, routes) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Import from default vrf into vrf ISR on R1") + + input_dict_isr={} + DUT = ["r1", "r2"] + VRFS = ["ISR", "ISR"] + AS_NUM = [100, 100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "default" + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify that default vrf's imported routes are installed " + "in RIB/FIB of vrf ISR on R1:") + + input_routes_r3 = { + "r3": { + "static_routes": [{ + "network": [ + NETWORK3_1[addr_type], \ + NETWORK3_2[addr_type], \ + NETWORK3_3[addr_type], \ + NETWORK3_4[addr_type] + ], + "vrf": "ISR" + }] + } + } + + input_routes_r4 = { + "r4": { + "static_routes": [{ + "network": [ + NETWORK4_1[addr_type], \ + NETWORK4_2[addr_type], \ + NETWORK4_3[addr_type], \ + NETWORK4_4[addr_type] + ], + "vrf": "ISR" + }] + } + } + + INPUT_DICT_VRF = [input_routes_r3, input_routes_r4] + + for routes in INPUT_DICT_VRF: + result = verify_bgp_rib(tgen, addr_type, "r1", routes) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + result = verify_fib_routes(tgen, addr_type, "r1", routes) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + intf_r2_r1 = topo["routers"]["r2"]["links"]["r1-link1"] + for addr_type in ADDR_TYPES: + + step("Create a loopback10 interface on R1 with below IP address and " + "associate with vrf ISR:") + + create_interface_in_kernel(tgen, "r1", "loopback2", + LOOPBACK_2[addr_type], + "ISR", + LOOPBACK_2["{}_mask".\ + format(addr_type)]) + + for addr_type in ADDR_TYPES: + + step("On router R1 Change the next-hop of static routes in vrf " + "ISR to LOOPBACK_1") + + input_routes_r1= { + "r1": { + "static_routes":[ + { + "network": [NETWORK1_3[addr_type], NETWORK1_4[addr_type]], + "next_hop":"Null0", + "delete": True + } + ] + } + } + + result = create_static_routes(tgen, input_routes_r1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + input_routes_r1= { + "r1": { + "static_routes":[ + { + "network": [NETWORK1_3[addr_type], NETWORK1_4[addr_type]], + "next_hop": (intf_r2_r1[addr_type]).split("/")[0] + } + ] + } + } + + result = create_static_routes(tgen, input_routes_r1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify that, though R1 originating BGP routes with next-hop" + " 24.1.1.2/24::1:2, which is local to R2(but in default vrf)" + ", R2 must receives and install all routes from R1 in vrf ISR.") + step("Verify on R2, that it now rejects 10.10.10.x routes originated " + "from R1. As next-hop IP is local to R2's vrf ISR.") + + input_routes_r1= { + "r1": { + "static_routes":[ + { + "network": [NETWORK1_3[addr_type], NETWORK1_4[addr_type]], + "vrf": "ISR" + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Routes are still present \n Error {}". \ + format(tc_name, result)) + + write_test_footer(tc_name) + + +def test_dynamic_imported_matching_prefix_based_on_community_list_p0(request): + """ + TC7_FUNC_7: + 1.5.7. Verify matching a prefix based on community attribute and + importing it by stripping off this value + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + for addr_type in ADDR_TYPES: + + step("Configure route-map to set community attribute for a specific" + "prefix on R1 in vrf ISR") + + input_dict_pf = { + "r1": { + "prefix_lists": { + addr_type: { + "pflist_ABC_{}".format(addr_type): [{ + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit" + }] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + input_dict_cl = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "expanded", + "action": "permit", + "name": "COMM", + "value": "100:100" + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_XYZ_{}".format(addr_type): [{ + "action": "permit", + "match": { + addr_type: { + "prefix_lists": + "pflist_ABC_{}".format(addr_type) + } + }, + "set": { + "community": {"num": "100:100"} + } + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Apply this route-map on R1 to vrf ISR while redistributing the" + " prefixes into BGP") + + input_dict_1={} + DUT = ["r1"] + VRFS = ["ISR"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [{ + "redist_type": "static", + "attribute": { + "route-map" : "rmap_XYZ_{}".\ + format(addr_type) + } + } + ] + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Configure another route-map for filtering the prefixes based on" + " community attribute while importing into default vrf") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [{ + "action": "permit", + "match": { + "community_list": {"id": "COMM"} + }, + "set": { + "community": {"num": "none"} + } + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Apply the route-map while Importing vrf ISR's prefixes into " + "default vrf on router R1:") + + input_dict_isr={} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR" + } + } + } + } + }) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type) + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify on R1 that only prefixes with community value 100:100" + "in vrf ISR are imported to vrf default. While importing, the" + " community value has been stripped off:") + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + input_dict_comm = { + "community": "100:100" + } + + result = verify_bgp_community(tgen, addr_type, dut, [NETWORK1_1[addr_type]], + input_dict_comm, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Error: Commnunity is not stipped off, {}".format( + tc_name, result)) + + for addr_type in ADDR_TYPES: + + step("Remove/re-add route-map XYZ from redistribution.") + + input_dict_1={} + DUT = ["r1"] + VRFS = ["ISR"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [{ + "redist_type": "static", + "attribute": { + "route-map" : "rmap_XYZ_{}".\ + format(addr_type) + }, + "delete": True + }] + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify that all the routes disappear from vrf default when " + "route-map is removed from redistribution, and appear again " + "when route-map is re-added to redistribution in vrf ISR.") + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Error : Routes are still present \n {}".\ + format(tc_name, result)) + + for addr_type in ADDR_TYPES: + + input_dict_1={} + DUT = ["r1"] + VRFS = ["ISR"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [{ + "redist_type": "static", + "attribute": { + "route-map" : "rmap_XYZ_{}".\ + format(addr_type) + } + }] + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Remove/re-add route-map IMP form import statement.") + + input_dict_isr={} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR" + } + } + } + } + }) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type), + "delete": True + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify that when route-map IMP is removed all the prefixes of" + " vrf ISR are imported to vrf default. However when route-map " + "IMP is re-added only 11.11.11.1 and 11:11::1 (with community " + "value) are imported.") + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + input_dict_isr={} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR" + } + } + } + } + }) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type) + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Delete/Re-add prefix-list ABC.") + + input_dict_pf = { + "r1": { + "prefix_lists": { + addr_type: { + "pflist_ABC_{}".format(addr_type): [{ + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit", + "delete": True + }] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Error : Routes are still present \n {}".\ + format(tc_name, result)) + + input_dict_pf["r1"]["prefix_lists"][addr_type]["pflist_ABC_{}".\ + format(addr_type)][0]["delete"]=False + + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + step("Delete/Re-add community-list COMM.") + + input_dict_cl = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "expanded", + "action": "permit", + "name": "COMM", + "value": "100:100", + "delete": True + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Error : Routes are still present \n {}".\ + format(tc_name, result)) + + input_dict_cl["r1"]["bgp_community_lists"][0]["delete"]=False + + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + step("Delete/Re-add route-map XYZ.") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_XYZ_{}".format(addr_type): [{ + "action": "permit", + "match": { + addr_type: { + "prefix_lists": + "pflist_ABC_{}".format(addr_type) + } + }, + "set": { + "community": {"num": "100:100"} + }, + "delete": True + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Error : Routes are still present \n {}".\ + format(tc_name, result)) + + input_dict_rm["r1"]["route_maps"]["rmap_XYZ_{}".format(addr_type)][0]["delete"]=False + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + step("Delete/Re-add route-map IMP.") + + input_dict_rm2 = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [{ + "action": "permit", + "match": { + "community_list": {"id": "COMM"} + }, + "set": { + "community": {"num": "none"} + }, + "delete": True + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm2) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Error : Routes are still present \n {}".\ + format(tc_name, result)) + + input_dict_rm2["r1"]["route_maps"]["rmap_IMP_{}".format(addr_type)][0]["delete"]=False + + result = create_route_maps(tgen, input_dict_rm2) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + write_test_footer(tc_name) + + +def test_routemap_operatons_with_dynamic_import_p0(request): + """ + TC8_FUNC_8: + 1.5.8. Verify the route-map operation along with dynamic import command. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + for addr_type in ADDR_TYPES: + + step("Configure route-map to set community attribute for a specific" + "prefix on R1 in vrf ISR") + + input_dict_pf = { + "r1": { + "prefix_lists": { + addr_type: { + "pflist_ABC_{}".format(addr_type): [{ + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit" + }] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + input_dict_cl = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "expanded", + "action": "permit", + "name": "COMM", + "value": "100:100" + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_XYZ_{}".format(addr_type): [{ + "action": "permit", + "match": { + addr_type: { + "prefix_lists": + "pflist_ABC_{}".format(addr_type) + } + }, + "set": { + "community": {"num": "100:100"} + } + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Apply this route-map on R1 to vrf ISR while redistributing the" + " prefixes into BGP") + + input_dict_1={} + DUT = ["r1"] + VRFS = ["ISR"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [{ + "redist_type": "static", + "attribute": { + "route-map" : "rmap_XYZ_{}".\ + format(addr_type) + } + } + ] + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Configure another route-map for filtering the prefixes based on" + " community attribute while importing into default vrf") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [{ + "action": "permit", + "match": { + "community_list": {"id": "COMM"} + }, + "set": { + "community": {"num": "500:500"} + } + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Apply the route-map while Importing vrf ISR's prefixes into " + "default vrf on router R1:") + + input_dict_isr={} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR" + } + } + } + } + }) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type) + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify on R1 that only prefixes with community value 100:100" + "in vrf ISR are imported to vrf default. While importing, the" + " community value has been stripped off:") + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Applying route-map first followed by import VRF command.") + step("Apply the route-map while Importing vrf ISR's prefixes into " + "default vrf on router R1:") + + input_dict_isr={} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR", + "delete": True + } + } + } + } + }) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type) + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify that until 'import VRF command' is not configured, " + "routes are not imported. After configuring 'import VRF command'" + " repeat step-4 for verification") + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Error : Routes are still present \n {}".\ + format(tc_name, result)) + + for addr_type in ADDR_TYPES: + + input_dict_isr={} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR" + } + } + } + } + }) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type) + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Delete/re-add import vrf ISR command multiple times in default" + "vrf.") + + input_dict_isr={} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR", + "delete": True + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + step("Verify that when import vrf ISR command is deleted, " + "all routes of vrf ISR disappear from default vrf and " + "when it's re-configured, repeat step-4 for verification.") + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Routes are still present, Error {}". \ + format(tc_name, result)) + + input_dict_isr["r1"]["bgp"][0]["address_family"][addr_type]["unicast"][ + "import"]["delete"]=False + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, ( + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result)) + + for addr_type in ADDR_TYPES: + + step("Delete and re-configure route-map IMP from global config when " + "import and route-maps are applied in a ISR vrf.") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [{ + "action": "permit", + "match": { + "community_list": {"id": "COMM"} + }, + "set": { + "community": {"num": "500:500"} + }, + "delete": True + }] + } + } + } + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Routes are still present, Error {}". \ + format(tc_name, result)) + + input_dict_rm["r1"]["route_maps"]["rmap_IMP_{}".\ + format(addr_type)][0]["delete"]=False + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + input_dict_comm = { + "community": "500:500" + } + + result = verify_bgp_community(tgen, addr_type, dut, [NETWORK1_1[addr_type]], + input_dict_comm) + assert result is True, ( + "Testcase {} : Failed \n Error: {}".format( + tc_name, result)) + + write_test_footer(tc_name) + + +def test_verify_cli_json_p1(request): + """ + TC8_FUNC_9: + 1.5.9. Verifying the JSON outputs for all supported commands: + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + input_dict = { + "r1":{ + "cli": ["show bgp vrf default ipv4 summary", + "show bgp vrf all ipv6 summary", + "show bgp neighbors" + ] + } + } + + result = verify_cli_json(tgen, input_dict) + 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_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py new file mode 100755 index 0000000000..6c106060b8 --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py @@ -0,0 +1,962 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2020 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 BGP Multi-VRF Dynamic Route Leaking: + +1. Verify that Changing route-map configurations(match/set clauses) on + the fly it takes immediate effect. +2. Verify BGP best path selection algorithm works fine when + routes are imported from ISR to default vrf and vice versa. +""" + +import os +import sys +import json +import time +import pytest +import platform + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) +sys.path.append(os.path.join(CWD, '../lib/')) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, write_test_header, check_address_types, + write_test_footer, + verify_rib, step, create_route_maps, + create_static_routes, stop_router, start_router, + create_prefix_lists, + create_bgp_community_lists, + check_router_status, + get_frr_ipv6_linklocal, + shutdown_bringup_interface +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, create_router_bgp, + verify_bgp_community, verify_bgp_attributes, + verify_best_path_as_per_bgp_attribute, verify_bgp_rib +) +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/bgp_vrf_dynamic_route_leak_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 +NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"} +NETWORK3_3 = {"ipv4": "50.50.50.5/32", "ipv6": "50:50::5/128"} +NETWORK3_4 = {"ipv4": "50.50.50.50/32", "ipv6": "50:50::50/128"} + +PREFERRED_NEXT_HOP = "global" + + +class CreateTopo(Topo): + """ + Test BasicTopo - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Run these tests for kernel version 4.19 or above + if version_cmp(platform.release(), '4.19') < 0: + error_msg = ('BGP vrf dynamic route leak tests will not run ' + '(have kernel "{}", but it requires >= 4.19)'.\ + format(platform.release())) + pytest.skip(error_msg) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + 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) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info("Testsuite end time: {}". + format(time.asctime(time.localtime(time.time())))) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + +def test_bgp_best_path_with_dynamic_import_p0(request): + """ + TC6_FUNC_6: + 1.5.6. Verify BGP best path selection algorithm works fine when + routes are imported from ISR to default vrf and vice versa. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + for addr_type in ADDR_TYPES: + + step("Redistribute configured static routes into BGP process" + " on R1/R2 and R3") + + input_dict_1={} + DUT = ["r1", "r2", "r3", "r4"] + VRFS = ["ISR", "ISR", "default", "default"] + AS_NUM = [100, 100, 300, 400] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [{ + "redist_type": "static" + }] + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Import from default vrf into vrf ISR on R1 and R2 as below") + + input_dict_vrf={} + DUT = ["r1", "r2"] + VRFS = ["ISR", "ISR"] + AS_NUM = [100, 100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_vrf.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "default" + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_vrf) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + input_dict_default={} + DUT = ["r1", "r2"] + VRFS = ["default", "default"] + AS_NUM = [100, 100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_default.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR" + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_default) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + step("Verify ECMP/Next-hop/Imported routes Vs Locally originated " + "routes/eBGP routes vs iBGP routes --already covered in almost" + " all tests") + + for addr_type in ADDR_TYPES: + + step("Verify Pre-emption") + + input_routes_r3 = { + "r3": { + "static_routes": [{ + "network": [ + NETWORK3_3[addr_type] + ] + }] + } + } + + intf_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"]["interface"] + intf_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"]["interface"] + + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + nh_r3_r1 = get_frr_ipv6_linklocal(tgen, "r3", intf=intf_r3_r1) + nh_r4_r1 = get_frr_ipv6_linklocal(tgen, "r4", intf=intf_r4_r1) + else: + nh_r3_r1 = topo["routers"]["r3"]["links"]\ + ["r1-link1"][addr_type].split("/")[0] + nh_r4_r1 = topo["routers"]["r4"]["links"]\ + ["r1-link1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r3, + next_hop=[nh_r4_r1]) + assert result is True, ( + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result)) + + step("Shutdown interface connected to r1 from r4:") + shutdown_bringup_interface(tgen, 'r4', intf_r4_r1, False) + + for addr_type in ADDR_TYPES: + + input_routes_r3 = { + "r3": { + "static_routes": [{ + "network": [ + NETWORK3_3[addr_type] + ] + }] + } + } + + intf_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"]["interface"] + intf_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"]["interface"] + + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + nh_r3_r1 = get_frr_ipv6_linklocal(tgen, "r3", intf=intf_r3_r1) + nh_r4_r1 = get_frr_ipv6_linklocal(tgen, "r4", intf=intf_r4_r1) + else: + nh_r3_r1 = topo["routers"]["r3"]["links"]\ + ["r1-link1"][addr_type].split("/")[0] + nh_r4_r1 = topo["routers"]["r4"]["links"]\ + ["r1-link1"][addr_type].split("/")[0] + + step("Verify next-hop is changed") + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r3, + next_hop=[nh_r3_r1]) + assert result is True, ( + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result)) + + step("Bringup interface connected to r1 from r4:") + shutdown_bringup_interface(tgen, 'r4', intf_r4_r1, True) + + for addr_type in ADDR_TYPES: + + input_routes_r3 = { + "r3": { + "static_routes": [{ + "network": [ + NETWORK3_3[addr_type] + ] + }] + } + } + + intf_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"]["interface"] + intf_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"]["interface"] + + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + nh_r3_r1 = get_frr_ipv6_linklocal(tgen, "r3", intf=intf_r3_r1) + nh_r4_r1 = get_frr_ipv6_linklocal(tgen, "r4", intf=intf_r4_r1) + else: + nh_r3_r1 = topo["routers"]["r3"]["links"]\ + ["r1-link1"][addr_type].split("/")[0] + nh_r4_r1 = topo["routers"]["r4"]["links"]\ + ["r1-link1"][addr_type].split("/")[0] + + step("Verify next-hop is not chnaged aftr shutdown:") + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r3, + next_hop=[nh_r3_r1]) + assert result is True, ( + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result)) + + step("Active-Standby scenario(as-path prepend and Local pref)") + + for addr_type in ADDR_TYPES: + + step("Create prefix-list") + + input_dict_pf = { + "r1": { + "prefix_lists": { + addr_type: { + "pf_ls_{}".format(addr_type): [{ + "seqid": 10, + "network": NETWORK3_4[addr_type], + "action": "permit" + }] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Create route-map to match prefix-list and set localpref 500") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_PATH1_{}".format(addr_type): [{ + "action": "permit", + "seq_id": 10, + "match": { + addr_type: { + "prefix_lists": + "pf_ls_{}".format(addr_type) + } + }, + "set": { + "locPrf": 500 + } + }] + } + } + } + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + step("Create route-map to match prefix-list and set localpref 600") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_PATH2_{}".format(addr_type): [{ + "action": "permit", + "seq_id": 20, + "match": { + addr_type: { + "prefix_lists": + "pf_ls_{}".format(addr_type) + } + }, + "set": { + "locPrf": 600 + } + }] + } + } + } + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + input_dict_rma={ + "r1": { + "bgp": + [ + { + "local_as": "100", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "route_maps": [{ + "name": "rmap_PATH1_{}".\ + format(addr_type), + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r1-link1": { + "route_maps": [{ + "name": "rmap_PATH2_{}".\ + format(addr_type), + "direction": "in" + }] + } + } + } + } + } + } + } + } + ]} + } + + result = create_router_bgp(tgen, topo, input_dict_rma) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + dut = "r1" + attribute = "locPrf" + + for addr_type in ADDR_TYPES: + + step("Verify bestpath is installed as per highest localpref") + + input_routes_r3 = { + "r3": { + "static_routes": [{ + "network": [ + NETWORK3_3[addr_type], \ + NETWORK3_4[addr_type] + ] + }] + } + } + + result = verify_best_path_as_per_bgp_attribute(tgen, addr_type, dut, + input_routes_r3, + attribute) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Create route-map to match prefix-list and set localpref 700") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_PATH1_{}".format(addr_type): [{ + "action": "permit", + "seq_id": 10, + "match": { + addr_type: { + "prefix_lists": + "pf_ls_{}".format(addr_type) + } + }, + "set": { + "locPrf": 700 + } + }] + } + } + } + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify bestpath is changed as per highest localpref") + + input_routes_r3 = { + "r3": { + "static_routes": [{ + "network": [ + NETWORK3_3[addr_type], \ + NETWORK3_4[addr_type] + ] + }] + } + } + + result = verify_best_path_as_per_bgp_attribute(tgen, addr_type, dut, + input_routes_r3, + attribute) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Create route-map to match prefix-list and set as-path prepend") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_PATH2_{}".format(addr_type): [{ + "action": "permit", + "seq_id": 20, + "match": { + addr_type: { + "prefix_lists": + "pf_ls_{}".format(addr_type) + } + }, + "set": { + "localpref": 700, + "path": { + "as_num": "111", + "as_action": "prepend" + } + } + }] + } + } + } + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + attribute = "path" + + for addr_type in ADDR_TYPES: + + step("Verify bestpath is changed as per shortest as-path") + + input_routes_r3 = { + "r3": { + "static_routes": [{ + "network": [ + NETWORK3_3[addr_type], \ + NETWORK3_4[addr_type] + ] + }] + } + } + + result = verify_best_path_as_per_bgp_attribute(tgen, addr_type, dut, + input_routes_r3, + attribute) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_modify_route_map_match_set_clauses_p1(request): + """ + TC13_CHAOS_4: + 1.5.13. Verify that Changing route-map configurations(match/set clauses) on + the fly it takes immediate effect. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + for addr_type in ADDR_TYPES: + + step("Configure route-map to set community attribute for a specific" + "prefix on R1 in vrf ISR") + + input_dict_pf = { + "r1": { + "prefix_lists": { + addr_type: { + "pflist_ABC_{}".format(addr_type): [{ + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit" + }] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + input_dict_cl = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "expanded", + "action": "permit", + "name": "COMM", + "value": "100:100" + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_XYZ_{}".format(addr_type): [{ + "action": "permit", + "match": { + addr_type: { + "prefix_lists": + "pflist_ABC_{}".format(addr_type) + } + }, + "set": { + "community": {"num": "100:100"} + } + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Apply this route-map on R1 to vrf ISR while redistributing the" + " prefixes into BGP") + + input_dict_1={} + DUT = ["r1"] + VRFS = ["ISR"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [{ + "redist_type": "static", + "attribute": { + "route-map" : "rmap_XYZ_{}".\ + format(addr_type) + } + } + ] + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Configure another route-map for filtering the prefixes based on" + " community attribute while importing into default vrf") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [{ + "action": "permit", + "seq_id": 10, + "match": { + "community_list": {"id": "COMM"} + }, + "set": { + "community": {"num": "none"} + } + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Apply the route-map while Importing vrf ISR's prefixes into " + "default vrf on router R1:") + + input_dict_isr={} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR" + } + } + } + } + }) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type) + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify on R1 that only prefixes with community value 100:100" + "in vrf ISR are imported to vrf default. While importing, the" + " community value has been stripped off:") + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Add set clause in route-map IMP:") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [{ + "action": "permit", + "seq_id": 10, + "match": { + "community_list": {"id": "COMM"} + }, + "set": { + "large_community": {"num": "100:100:100"}, + "locPrf": 500, + "path": { + "as_num": "100 100", + "as_action": "prepend" + } + } + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify that as we continue adding different attributes " + "step-by-step in route-map IMP those attributes gets " + "attached to prefixes:") + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + input_dict_comm = { + "largeCommunity": "100:100:100" + } + + result = verify_bgp_community(tgen, addr_type, dut, [NETWORK1_1[addr_type]], + input_dict_comm) + assert result is True, ( + "Testcase {} : Failed \n Error {}".format( + tc_name, result)) + + input_rmap = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [ + { + "set": { + "locPrf": 500 + } + } + ] + } + } + } + + result = verify_bgp_attributes(tgen, addr_type, "r1",\ + [NETWORK1_1[addr_type]], + rmap_name="rmap_IMP_{}".format(addr_type),\ + input_dict=input_rmap) + assert result is True, "Testcase : Failed \n Error: {}".format( + tc_name, result) + + step("Change community-list to match a different value then " + "100:100.") + + input_dict_cl = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "expanded", + "action": "permit", + "name": "COMM", + "value": "100:100", + "delete": True + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Error : Routes are still " + "present {}".\ + 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/lib/bgp.py b/tests/topotests/lib/bgp.py index 316d4cf414..e2f887bb89 100644 --- a/tests/topotests/lib/bgp.py +++ b/tests/topotests/lib/bgp.py @@ -456,6 +456,15 @@ def __create_bgp_unicast_neighbor( cmd = "no {}".format(cmd) config_data.append(cmd) + import_vrf_data = addr_data.setdefault("import", {}) + if import_vrf_data: + cmd = "import vrf {}".format(import_vrf_data["vrf"]) + + del_action = import_vrf_data.setdefault("delete", False) + if del_action: + cmd = "no {}".format(cmd) + config_data.append(cmd) + if "neighbor" in addr_data: neigh_data = __create_bgp_neighbor( topo, input_dict, router, addr_type, add_neigh @@ -2610,6 +2619,7 @@ def verify_bgp_rib(tgen, addr_type, dut, input_dict, next_hop=None, aspath=None) if not isinstance(next_hop, list): next_hop = [next_hop] list1 = next_hop + found_hops = [ rib_r["ip"] for rib_r in rib_routes_json["routes"][st_rt][0][ @@ -2617,6 +2627,7 @@ def verify_bgp_rib(tgen, addr_type, dut, input_dict, next_hop=None, aspath=None) ] ] list2 = found_hops + missing_list_of_nexthops = set(list2).difference(list1) additional_nexthops_in_required_nhs = set( list1 diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index d77946b1ed..1846d43138 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -32,6 +32,7 @@ from tempfile import mkdtemp import os import sys +import ConfigParser import traceback import socket import ipaddress @@ -1237,7 +1238,8 @@ def interface_status(tgen, topo, input_dict): return True -def retry(attempts=3, wait=2, return_is_str=True, initial_wait=0, return_is_dict=False): +def retry(attempts=3, wait=2, return_is_str=True, initial_wait=0, + return_is_dict=False): """ Retries function execution, if return is an errormsg or exception @@ -1249,11 +1251,13 @@ def retry(attempts=3, wait=2, return_is_str=True, initial_wait=0, return_is_dict """ def _retry(func): + @wraps(func) def func_retry(*args, **kwargs): - _wait = kwargs.pop("wait", wait) - _attempts = kwargs.pop("attempts", attempts) + _wait = kwargs.pop('wait', wait) + _attempts = kwargs.pop('attempts', attempts) _attempts = int(_attempts) + expected = True if _attempts < 0: raise ValueError("attempts must be 0 or greater") @@ -1261,40 +1265,40 @@ def retry(attempts=3, wait=2, return_is_str=True, initial_wait=0, return_is_dict 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) - _return_is_dict = kwargs.pop("return_is_str", return_is_dict) + _return_is_str = kwargs.pop('return_is_str', return_is_str) + _return_is_dict = kwargs.pop('return_is_str', return_is_dict) for i in range(1, _attempts + 1): try: - _expected = kwargs.setdefault("expected", True) - kwargs.pop("expected") + _expected = kwargs.setdefault('expected', True) + if _expected is False: + expected = _expected + kwargs.pop('expected') ret = func(*args, **kwargs) - logger.debug("Function returned %s" % ret) + logger.debug("Function returned %s", ret) if _return_is_str and isinstance(ret, bool) and _expected: return ret - if ( - isinstance(ret, str) or isinstance(ret, unicode) - ) and _expected is False: + if (isinstance(ret, str) or isinstance(ret, unicode)) and _expected is False: return ret if _return_is_dict and isinstance(ret, dict): return ret - if _attempts == i: + if _attempts == i and expected: generate_support_bundle() return ret except Exception as err: - if _attempts == i: + if _attempts == i and expected: generate_support_bundle() - logger.info("Max number of attempts (%r) reached", _attempts) + 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)) + logger.info("Retry [#%r] after sleeping for %ss" + % (i, _wait)) sleep(_wait) - func_retry._original = func return func_retry - return _retry @@ -2517,7 +2521,7 @@ def verify_rib( errormsg(str) or True """ - logger.info("Entering lib API: verify_rib()") + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) router_list = tgen.routers() additional_nexthops_in_required_nhs = [] @@ -2832,7 +2836,263 @@ def verify_rib( " routes are: {}\n".format(addr_type, dut, found_routes) ) - logger.info("Exiting lib API: verify_rib()") + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2) +def verify_fib_routes(tgen, addr_type, dut, input_dict, next_hop=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 + advertise_networks_using_network_command() and will verify next_hop and + each prefix/routes is present in "show ip/ipv6 fib json" + command o/p. + + Parameters + ---------- + * `tgen` : topogen object + * `addr_type` : ip type, ipv4/ipv6 + * `dut`: Device Under Test, for which user wants to test the data + * `input_dict` : input dict, has details of static routes + * `next_hop`[optional]: next_hop which needs to be verified, + default: static + + Usage + ----- + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": ["1.1.1.1/32], + "next_hop": "Null0", + "vrf": "RED" + }] + } + } + result = result = verify_fib_routes(tgen, "ipv4, "r1", input_routes_r1) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + router_list = tgen.routers() + for routerInput in input_dict.keys(): + for router, rnode in router_list.iteritems(): + if router != dut: + continue + + logger.info("Checking router %s FIB routes:", router) + + # Verifying RIB routes + if addr_type == "ipv4": + command = "show ip fib" + else: + command = "show ipv6 fib" + + found_routes = [] + missing_routes = [] + + if "static_routes" in input_dict[routerInput]: + static_routes = input_dict[routerInput]["static_routes"] + + for static_route in static_routes: + if "vrf" in static_route and static_route["vrf"] is not None: + + logger.info( + "[DUT: {}]: Verifying routes for VRF:" + " {}".format(router, static_route["vrf"]) + ) + + cmd = "{} vrf {}".format(command, static_route["vrf"]) + + else: + cmd = "{}".format(command) + + cmd = "{} json".format(cmd) + + rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True) + + # Verifying output dictionary rib_routes_json is not empty + if bool(rib_routes_json) is False: + errormsg = "[DUT: {}]: No route found in fib".format(router) + return errormsg + + network = static_route["network"] + if "no_of_ip" in static_route: + no_of_ip = static_route["no_of_ip"] + else: + no_of_ip = 1 + + # Generating IPs for verification + ip_list = generate_ips(network, no_of_ip) + st_found = False + nh_found = False + + for st_rt in ip_list: + st_rt = str(ipaddress.ip_network(unicode(st_rt))) + #st_rt = str(ipaddr.IPNetwork(unicode(st_rt))) + + _addr_type = validate_ip_address(st_rt) + if _addr_type != addr_type: + continue + + if st_rt in rib_routes_json: + st_found = True + found_routes.append(st_rt) + + if next_hop: + if type(next_hop) is not list: + next_hop = [next_hop] + + count = 0 + for nh in next_hop: + for nh_dict in rib_routes_json[st_rt][0][ + "nexthops" + ]: + if nh_dict["ip"] != nh: + continue + else: + count += 1 + + if count == len(next_hop): + nh_found = True + else: + missing_routes.append(st_rt) + errormsg = ( + "Nexthop {} is Missing" + " for route {} in " + "RIB of router {}\n".format( + next_hop, st_rt, dut + ) + ) + return errormsg + + else: + missing_routes.append(st_rt) + + if len(missing_routes) > 0: + errormsg = "[DUT: {}]: Missing route in FIB:" " {}".format( + dut, missing_routes + ) + return errormsg + + if nh_found: + logger.info( + "Found next_hop {} for all routes in RIB" + " of router {}\n".format(next_hop, dut) + ) + + if found_routes: + logger.info( + "[DUT: %s]: Verified routes in FIB, found" " routes are: %s\n", + dut, + found_routes, + ) + + continue + + if "bgp" in input_dict[routerInput]: + if ( + "advertise_networks" + not in input_dict[routerInput]["bgp"]["address_family"][addr_type][ + "unicast" + ] + ): + continue + + found_routes = [] + missing_routes = [] + advertise_network = input_dict[routerInput]["bgp"]["address_family"][ + addr_type + ]["unicast"]["advertise_networks"] + + # Continue if there are no network advertise + if len(advertise_network) == 0: + continue + + for advertise_network_dict in advertise_network: + if "vrf" in advertise_network_dict: + cmd = "{} vrf {} json".format(command, static_route["vrf"]) + else: + cmd = "{} json".format(command) + + rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True) + + # Verifying output dictionary rib_routes_json is not empty + if bool(rib_routes_json) is False: + errormsg = "No route found in rib of router {}..".format(router) + return errormsg + + start_ip = advertise_network_dict["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 + ip_list = generate_ips(start_ip, no_of_network) + st_found = False + nh_found = False + + for st_rt in ip_list: + #st_rt = str(ipaddr.IPNetwork(unicode(st_rt))) + st_rt = str(ipaddress.ip_network(unicode(st_rt))) + + _addr_type = validate_ip_address(st_rt) + if _addr_type != addr_type: + continue + + if st_rt in rib_routes_json: + st_found = True + found_routes.append(st_rt) + + if next_hop: + if type(next_hop) is not list: + next_hop = [next_hop] + + count = 0 + for nh in next_hop: + for nh_dict in rib_routes_json[st_rt][0]["nexthops"]: + if nh_dict["ip"] != nh: + continue + else: + count += 1 + + if count == len(next_hop): + nh_found = True + else: + missing_routes.append(st_rt) + errormsg = ( + "Nexthop {} is Missing" + " for route {} in " + "RIB of router {}\n".format(next_hop, st_rt, dut) + ) + return errormsg + else: + missing_routes.append(st_rt) + + if len(missing_routes) > 0: + errormsg = "[DUT: {}]: Missing route in FIB: " "{} \n".format( + dut, missing_routes + ) + return errormsg + + if nh_found: + logger.info( + "Found next_hop {} for all routes in RIB" + " of router {}\n".format(next_hop, dut) + ) + + if found_routes: + logger.info( + "[DUT: {}]: Verified routes FIB" + ", found routes are: {}\n".format(dut, found_routes) + ) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return True