diff --git a/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py b/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py index fd3e7fd7d3..087ba21e5e 100755 --- a/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py +++ b/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py @@ -295,7 +295,7 @@ def test_modify_ecmp_max_paths(request, ecmp_num, test_type): addr_type, dut, input_dict_1, - next_hop=NEXT_HOPS[addr_type], + next_hop=NEXT_HOPS[addr_type][:int(ecmp_num)], protocol=protocol, ) assert result is True, "Testcase {} : Failed \n Error: {}".format( diff --git a/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py b/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py index 94ffc71ef6..94409ff3e1 100755 --- a/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py +++ b/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py @@ -296,7 +296,7 @@ def test_modify_ecmp_max_paths(request, ecmp_num, test_type): addr_type, dut, input_dict_1, - next_hop=NEXT_HOPS[addr_type], + next_hop=NEXT_HOPS[addr_type][:int(ecmp_num)], protocol=protocol, ) assert result is True, "Testcase {} : Failed \n Error: {}".format( diff --git a/tests/topotests/bgp_multi_vrf_topo1/bgp_multi_vrf_topo1.json b/tests/topotests/bgp_multi_vrf_topo1/bgp_multi_vrf_topo1.json new file mode 100644 index 0000000000..327744d3a1 --- /dev/null +++ b/tests/topotests/bgp_multi_vrf_topo1/bgp_multi_vrf_topo1.json @@ -0,0 +1,884 @@ +{ + "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": { + "red1": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": {} + } + } + } + } + } + } + } + ] + }, + "blue1": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "BLUE_A", + "id": "1" + }, + { + "name": "BLUE_B", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link2": {} + } + } + } + } + } + } + } + ] + }, + "r1": { + "links": { + "red1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "red1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "blue1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "blue1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r2-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + }, + { + "name": "BLUE_A", + "id": "3" + }, + { + "name": "BLUE_B", + "id": "4" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": {} + } + }, + "r2": { + "dest_link": { + "r1-link1": + { "next_hop_self": true } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": {} + } + }, + "r2": { + "dest_link": { + "r1-link1": + { "next_hop_self": true } + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": {} + } + }, + "r2": { + "dest_link": { + "r1-link2": + { "next_hop_self": true } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": {} + } + }, + "r2": { + "dest_link": { + "r1-link2": + { "next_hop_self": true } + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": {} + } + }, + "r2": { + "dest_link": { + "r1-link3": + { "next_hop_self": true } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": {} + } + }, + "r2": { + "dest_link": { + "r1-link3": + { "next_hop_self": true } + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": {} + } + }, + "r2": { + "dest_link": { + "r1-link4": + { "next_hop_self": true } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": {} + } + }, + "r2": { + "dest_link": { + "r1-link4": + { "next_hop_self": true } + } + } + } + } + } + } + } + ] + }, + "r2": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r1-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r1-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r3-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + }, + { + "name": "BLUE_A", + "id": "3" + }, + { + "name": "BLUE_B", + "id": "4" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": {} + } + }, + "r3": { + "dest_link": { + "r2-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": {} + } + }, + "r3": { + "dest_link": { + "r2-link2": {} + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": {} + } + }, + "r3": { + "dest_link": { + "r2-link3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": {} + } + }, + "r3": { + "dest_link": { + "r2-link3": {} + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": {} + } + }, + "r3": { + "dest_link": { + "r2-link4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": {} + } + }, + "r3": { + "dest_link": { + "r2-link4": {} + } + } + } + } + } + } + } + ] + }, + "r3": { + "links": { + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r2-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "red2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "red2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "blue2-link1": {"ipv4": "auto", "ipv6": "autor3", "vrf": "BLUE_A"}, + "blue2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + }, + { + "name": "BLUE_A", + "id": "3" + }, + { + "name": "BLUE_B", + "id": "4" + } + ], + "bgp": + [ + { + "local_as": "200", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {} + } + }, + "red2": { + "dest_link": { + "r3-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {} + } + }, + "red2": { + "dest_link": { + "r3-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link2": {} + } + }, + "red2": { + "dest_link": { + "r3-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link2": {} + } + }, + "red2": { + "dest_link": { + "r3-link2": {} + } + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link3": {} + } + }, + "blue2": { + "dest_link": { + "r3-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link3": {} + } + }, + "blue2": { + "dest_link": { + "r3-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link4": {} + } + }, + "blue2": { + "dest_link": { + "r3-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link4": {} + } + }, + "blue2": { + "dest_link": { + "r3-link2": {} + } + } + } + } + } + } + } + ] + }, + "red2": { + "links": { + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link2": {} + } + } + } + } + } + } + } + ] + }, + "blue2": { + "links": { + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "BLUE_A", + "id": "1" + }, + { + "name": "BLUE_B", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link2": {} + } + } + } + } + } + } + } + ] + } + } +} diff --git a/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py b/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py new file mode 100755 index 0000000000..aabce04f24 --- /dev/null +++ b/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py @@ -0,0 +1,6300 @@ +#!/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: + +FUNC_1: + Within each VRF, each address must be unambiguous on DUT. +FUNC_2: + Different VRFs can have ambiguous/overlapping + addresses on DUT. +FUNC_3: + Create static routes(IPv4+IPv6) associated to specific VRFs + and verify on DUT that same prefixes are present in corresponding + routing table. +FUNC_4_&_5: + Each VRF should be mapped with a unique VLAN on DUT + for traffic segregation, when using a single physical interface. +FUNC_6: + Advertise same set of prefixes from different VRFs + and verify on remote router that these prefixes are not + leaking to each other +FUNC_7: + Redistribute Static routes and verify on remote routers + that routes are advertised within specific VRF instance, which + those static routes belong to. +FUNC_8: + Test end to end traffic isolation based on VRF tables. +FUNC_9: + Use static routes for inter-vrf communication + (route-leaking) on DUT. +FUNC_10: + Verify intra-vrf and inter-vrf communication between + iBGP peers. +FUNC_11: + Verify intra-vrf and inter-vrf communication + between eBGP peers. +FUNC_12_a: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. +FUNC_12_b: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. +FUNC_12_c: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. +FUNC_12_d: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. +FUNC_12_e: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. +FUNC_12_f: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. +FUNC_13: + Configure a route-map on DUT to match traffic based + on a VRF interfaces. +FUNC_14: + Test VRF-lite with Static+BGP originated routes. +FUNC_15: + Configure prefix-lists on DUT and apply to BGP peers to + permit/deny prefixes. +FUNC_16_1: + Configure a route-map on DUT to match traffic based various + match/set causes. +FUNC_16_2: + Configure a route-map on DUT to match traffic based various + match/set causes. +FUNC_16_3: + Configure a route-map on DUT to match traffic based various + match/set causes. +""" + +import os +import sys +import json +import time +import pytest +from copy import deepcopy + +# 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 mininet.topo import Topo + +from lib.common_config import ( + step, + verify_rib, + start_topology, + write_test_header, + check_address_types, + write_test_footer, + reset_config_on_routers, + create_route_maps, + create_static_routes, + create_prefix_lists, + create_interface_in_kernel, + kill_mininet_routers_process, + create_bgp_community_lists, + check_router_status, + apply_raw_config, +) + +from lib.topolog import logger +from lib.bgp import ( + clear_bgp, + verify_bgp_rib, + create_router_bgp, + verify_bgp_community, + verify_bgp_convergence, + verify_best_path_as_per_bgp_attribute, +) +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/bgp_multi_vrf_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": "1.1.1.1/32", "ipv6": "1::1/128"} +NETWORK1_2 = {"ipv4": "1.1.1.2/32", "ipv6": "1::2/128"} +NETWORK2_1 = {"ipv4": "2.1.1.1/32", "ipv6": "2::1/128"} +NETWORK2_2 = {"ipv4": "2.1.1.2/32", "ipv6": "2::2/128"} +NETWORK3_1 = {"ipv4": "3.1.1.1/32", "ipv6": "3::1/128"} +NETWORK3_2 = {"ipv4": "3.1.1.2/32", "ipv6": "3::2/128"} +NETWORK4_1 = {"ipv4": "4.1.1.1/32", "ipv6": "4::1/128"} +NETWORK4_2 = {"ipv4": "4.1.1.2/32", "ipv6": "4::2/128"} +NETWORK5_1 = {"ipv4": "5.1.1.1/32", "ipv6": "5::1/128"} +NETWORK5_2 = {"ipv4": "5.1.1.2/32", "ipv6": "5::2/128"} +NETWORK6_1 = {"ipv4": "6.1.1.1/32", "ipv6": "6::1/128"} +NETWORK6_2 = {"ipv4": "6.1.1.2/32", "ipv6": "6::2/128"} +NETWORK7_1 = {"ipv4": "7.1.1.1/32", "ipv6": "7::1/128"} +NETWORK7_2 = {"ipv4": "7.1.1.2/32", "ipv6": "7::2/128"} +NETWORK8_1 = {"ipv4": "8.1.1.1/32", "ipv6": "8::1/128"} +NETWORK8_2 = {"ipv4": "8.1.1.2/32", "ipv6": "8::2/128"} + +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + +LOOPBACK_1 = { + "ipv4": "10.10.10.10/32", + "ipv6": "10::10:10/128", + "ipv4_mask": "255.255.255.255", + "ipv6_mask": None, +} +LOOPBACK_2 = { + "ipv4": "20.20.20.20/32", + "ipv6": "20::20:20/128", + "ipv4_mask": "255.255.255.255", + "ipv6_mask": None, +} + + +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 + """ + + 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. + + # Kill stale mininet routers and process + kill_mininet_routers_process(tgen) + + # 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) + + 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_address_unambiguous_within_each_vrf_p0(request): + """ + FUNC_1: + Within each VRF, each address must be unambiguous on DUT. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configure a set of static routes(IPv4+IPv6) in " "RED_A on router RED-1") + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure the same static routes(IPv4+IPv6) with a TAG value" + "of 500 in RED_A on router RED-1" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "tag": 500, + "vrf": "RED_A", + } + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = { + "red1": { + "bgp": { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + }, + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that static routes(IPv4+IPv6) is overridden and doesn't" + " have duplicate entries within VRF RED_A on router RED-1" + ) + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "tag": 500, + "vrf": "RED_A", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, tag=500) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Make sure routes are not present in global routing table") + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Expected Behaviour: Routes are not " + "present on Global Routing table \n Error {}".format(tc_name, result) + ) + + write_test_footer(tc_name) + + +def test_ambiguous_overlapping_addresses_in_different_vrfs_p0(request): + """ + FUNC_2: + Different VRFs can have ambiguous/overlapping + addresses on DUT. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configure a set of static routes(IPv4+IPv6) in vrf RED_A" "on router RED-1") + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure the same static routes(IPv4+IPv6) with a" + " TAG value of 500 in vrf RED_B on router RED-1" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "tag": 500, + "vrf": "RED_B", + } + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that RED_A has the static routes without any" " TAG value") + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_1, tag=500, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info("Expected Behavior: {}".format(result)) + + step( + "Verify that RED_B has the same routes with TAG value " + "500 on same device RED-1" + ) + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "tag": 500, + "vrf": "RED_B", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, tag=500) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Make sure routes are not present in global routing table") + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Expected Behaviour: Routes are not " + "present on Global Routing table \n Error {}".format(tc_name, result) + ) + + write_test_footer(tc_name) + + +def test_static_routes_associated_to_specific_vrfs_p0(request): + """ + FUNC_3: + Create static routes(IPv4+IPv6) associated to specific VRFs + and verify on DUT that same prefixes are present in corresponding + routing table. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Configure a set of unique static(IPv4+IPv6) routes in vrf" + " RED_A on router RED-1" + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure set of unique static routes(IPv4+IPv6) in vrf " + "RED_B on router RED-1" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that static routes 1.x.x.x/32 and 1::x/128 appear" "in VRF RED_A table" + ) + step( + "Verify that static routes 2.x.x.x/32 and 2::x/128 appear" "in VRF RED_B table" + ) + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Verify that static routes 1.x.x.x/32 and 1::x/128 appear" "in VRF BLUE_A table" + ) + step( + "Verify that static routes 2.x.x.x/32 and 2::x/128 appear" "in VRF BLUE_B table" + ) + + for addr_type in ADDR_TYPES: + dut = "blue1" + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Make sure routes are not present in global routing table") + + for addr_type in ADDR_TYPES: + dut = "blue1" + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Expected Behaviour: Routes are not " + "present on Global Routing table \n Error {}".format(tc_name, result) + ) + + write_test_footer(tc_name) + + +def test_vrf_with_unique_physical_interface_p0(request): + """ + FUNC_4_&_5: + Each VRF should be mapped with a unique VLAN on DUT + for traffic segregation, when using a single physical interface. + + Each VRF should be mapped to a unique physical + interface(without VLAN tagging) on DUT for traffic segregation. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "R1 is receiving routes in 4 VRFs instances " + "(RED_A, RED_B, BLUE_A, BLUE_B) from RED_1 and BLUE_1." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise a set of unique BGP prefixes(IPv4+IPv6) from " + "routers RED_1 & BLUE_1 in each VRF using static redistribution" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Each VRF table on R2 should maintain it's associated " + "routes and and accordingly install in zebra" + ) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_prefixes_leaking_p0(request): + """ + FUNC_6: + Advertise same set of prefixes from different VRFs + and verify on remote router that these prefixes are not + leaking to each other + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Configure a set of static routes(IPv4+IPv6) in vrf " "RED_A on router RED-1") + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + }, + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + } + ] + }, + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure a set of static routes(IPv4+IPv6) in vrf " "BLUE_A on router BLUE-1" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + } + ] + }, + "blue1": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + } + ] + }, + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Configure the same set of static routes with a " + "metric value of 123 in vrf RED_B on router RED-1" + ) + step( + "Configure the same set of static routes with a " + "metric value of 123 in vrf BLUE_B on router BLUE-1" + ) + + input_dict_3 = { + "red1": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"metric": 123}, + } + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"metric": 123}, + } + ] + } + }, + }, + }, + ] + }, + "blue1": { + "bgp": [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"metric": 123}, + } + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"metric": 123}, + } + ] + } + }, + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on R1 that RED_A doesn't receive and static " + "route with metric value 123" + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + }, + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + } + ] + }, + } + + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + } + ] + }, + "blue1": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + } + ] + }, + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, addr_type, dut, input_dict_1, metric=123, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info("Expected Behavior: {}".format(result)) + + result = verify_rib(tgen, addr_type, dut, input_dict_2, metric=123) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, addr_type, dut, input_dict_2, metric=0, expected=False + ) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + logger.info("Expected Behavior: {}".format(result)) + + write_test_footer(tc_name) + + +def test_static_routes_advertised_within_specific_vrf_p0(request): + """ + FUNC_7: + Redistribute Static routes and verify on remote routers + that routes are advertised within specific VRF instance, which + those static routes belong to. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise a set of unique BGP prefixes(IPv4+IPv6) " + "through static redistribution into VRF RED_A and RED_B" + " from router RED-1." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise same as above set of BGP prefixes(IPv4+IPv6) " + "through static redistribution into VRF BLUE_A and BLUE_B" + " from router BLUE-1." + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that static routes are installed into vrfs RED_A" + "and RED_B tables only, not in global routing table of RED_1" + ) + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1, protocol="static") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Verify that static routes are installed into vrfs BLUE_A and" + "BLUE_B tables only, not in global routing table of BLUE_1." + ) + + for addr_type in ADDR_TYPES: + dut = "blue1" + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, protocol="static") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Verify on router R1, that each set of prefixes is received" + " into associated vrf tables only." + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_end_to_end_traffic_isolation_p0(request): + """ + FUNC_8: + Test end to end traffic isolation based on VRF tables. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from RED_1 " + "in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from from BLUE_1 in" + " vrf instances(BLUE_A and BLUE_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Use below commands to send prefixes with as-path prepend" + "VRF BLUE_A and BLUE_B from router BLUE-1." + ) + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "blue1": { + "route_maps": { + "ASP_{}".format(addr_type): [ + { + "action": "permit", + "set": {"path": {"as_num": 123, "as_action": "prepend"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Apply route-map to neighbours") + + input_dict_5 = { + "blue1": { + "bgp": [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link1": { + "route_maps": [ + { + "name": "ASP_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link1": { + "route_maps": [ + { + "name": "ASP_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link2": { + "route_maps": [ + { + "name": "ASP_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link2": { + "route_maps": [ + { + "name": "ASP_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on R1 that BLUE_A and BLUE_B VRFs are receiving the" + " prefixes with as-path 123 prepended." + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + input_dict_6 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + input_dict_7 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Use below commands to send prefixes with as-path prepend VRF" + " BLUE_A and BLUE_B from router BLUE-1." + ) + + input_dict_6 = { + "red2": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link1": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link1": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link2": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link2": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + }, + }, + ] + }, + "blue2": { + "bgp": [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link1": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link1": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + }, + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link2": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link2": { + "allowas-in": {"number_occurences": 2} + } + } + } + } + } + }, + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify that router RED-2 receives the prefixes in respective" " VRF tables.") + + for addr_type in ADDR_TYPES: + dut = "red2" + input_dict_6 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + dut = "blue2" + input_dict_7 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_static_routes_for_inter_vrf_route_leaking_p0(request): + """ + FUNC_9: + Use static routes for inter-vrf communication + (route-leaking) on DUT. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Configure unique loopback interfaces in VRFs RED_A " + "and RED_B on router RED_1." + ) + + for addr_type in ADDR_TYPES: + create_interface_in_kernel( + tgen, + "red1", + "loopback1", + LOOPBACK_1[addr_type], + "RED_A", + LOOPBACK_1["{}_mask".format(addr_type)], + ) + create_interface_in_kernel( + tgen, + "red1", + "loopback2", + LOOPBACK_2[addr_type], + "RED_B", + LOOPBACK_2["{}_mask".format(addr_type)], + ) + + step( + "Create a static routes in vrf RED_B on router RED_1 pointing" + " next-hop as interface's IP in vrf RED_A" + ) + + intf_red1_r11 = topo["routers"]["red1"]["links"]["r1-link1"]["interface"] + intf_red1_r10 = topo["routers"]["red1"]["links"]["r1-link2"]["interface"] + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": LOOPBACK_1[addr_type], + "interface": intf_red1_r10, + "nexthop_vrf": "RED_B", + "vrf": "RED_A", + }, + { + "network": LOOPBACK_2[addr_type], + "interface": intf_red1_r11, + "nexthop_vrf": "RED_A", + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that static routes are installed into vrfs RED_A" + "and RED_B tables only, not in global routing table of RED_1" + ) + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": LOOPBACK_1[addr_type], + "interface": intf_red1_r10, + "nexthop_vrf": "RED_B", + "vrf": "RED_A", + }, + { + "network": LOOPBACK_2[addr_type], + "interface": intf_red1_r11, + "nexthop_vrf": "RED_A", + "vrf": "RED_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1, protocol="static") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_inter_vrf_and_intra_vrf_communication_iBGP_p0(request): + """ + FUNC_10: + Verify intra-vrf and inter-vrf communication between + iBGP peers. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Configure unique loopback IP(IPv4+IPv6) in vrf RED_A on router" + " R1 and advertise it in BGP process using redistribute " + "connected command." + ) + + for addr_type in ADDR_TYPES: + create_interface_in_kernel( + tgen, + "r1", + "loopback1", + LOOPBACK_1[addr_type], + "RED_A", + LOOPBACK_1["{}_mask".format(addr_type)], + ) + + create_interface_in_kernel( + tgen, + "r1", + "loopback2", + LOOPBACK_2[addr_type], + "BLUE_A", + LOOPBACK_2["{}_mask".format(addr_type)], + ) + + step( + "Create a static routes in vrf RED_B on router RED_1 pointing" + " next-hop as interface's IP in vrf RED_A" + ) + + intf_r2_r12 = topo["routers"]["r2"]["links"]["r1-link1"]["interface"] + intf_r2_r10 = topo["routers"]["r2"]["links"]["r1-link3"]["interface"] + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r2": { + "static_routes": [ + { + "network": LOOPBACK_2[addr_type], + "interface": intf_r2_r10, + "nexthop_vrf": "BLUE_A", + "vrf": "RED_A", + }, + { + "network": LOOPBACK_1[addr_type], + "interface": intf_r2_r12, + "nexthop_vrf": "RED_A", + "vrf": "BLUE_A", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute connected..") + + input_dict_3 = {} + for dut in ["r1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + VRFS = ["RED_A", "BLUE_A"] + AS_NUM = [100, 100] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["r2"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + VRFS = ["RED_A", "BLUE_A"] + AS_NUM = [100, 100] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that static routes are installed into vrfs RED_A" + "and RED_B tables only, not in global routing table of RED_1" + ) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict = { + "r2": { + "static_routes": [ + { + "network": LOOPBACK_2[addr_type], + "interface": intf_r2_r10, + "nexthop_vrf": "BLUE_A", + "vrf": "RED_A", + }, + { + "network": LOOPBACK_1[addr_type], + "interface": intf_r2_r12, + "nexthop_vrf": "RED_A", + "vrf": "BLUE_A", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_inter_vrf_and_intra_vrf_communication_eBGP_p0(request): + """ + FUNC_11: + Verify intra-vrf and inter-vrf communication + between eBGP peers. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Configure unique loopback IP(IPv4+IPv6) in vrf RED_A on router" + " R2 and advertise it in BGP process using redistribute " + "connected command." + ) + + step( + "Configure unique loopback IP(IPv4+IPv6) in vrf BLUE_A on router" + " R2 and advertise it in BGP process using redistribute " + "connected command." + ) + + for addr_type in ADDR_TYPES: + create_interface_in_kernel( + tgen, + "r2", + "loopback1", + LOOPBACK_1[addr_type], + "RED_A", + LOOPBACK_1["{}_mask".format(addr_type)], + ) + create_interface_in_kernel( + tgen, + "r2", + "loopback2", + LOOPBACK_2[addr_type], + "BLUE_A", + LOOPBACK_2["{}_mask".format(addr_type)], + ) + + step( + "Create a static routes in vrf RED_B on router RED_1 pointing" + " next-hop as interface's IP in vrf RED_A" + ) + + intf_r3_r21 = topo["routers"]["r3"]["links"]["r2-link1"]["interface"] + intf_r3_r23 = topo["routers"]["r3"]["links"]["r2-link3"]["interface"] + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r3": { + "static_routes": [ + { + "network": LOOPBACK_2[addr_type], + "interface": intf_r3_r23, + "nexthop_vrf": "BLUE_A", + "vrf": "RED_A", + }, + { + "network": LOOPBACK_1[addr_type], + "interface": intf_r3_r21, + "nexthop_vrf": "RED_A", + "vrf": "BLUE_A", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["r3"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + VRFS = ["RED_A", "BLUE_A"] + AS_NUM = [200, 200] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Redistribute connected..") + + input_dict_3 = {} + for dut in ["r2"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + VRFS = ["RED_A", "BLUE_A"] + AS_NUM = [100, 100] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that static routes are installed into vrfs RED_A" + "and RED_B tables only, not in global routing table of RED_1" + ) + + for addr_type in ADDR_TYPES: + dut = "r3" + input_dict = { + "r3": { + "static_routes": [ + { + "network": LOOPBACK_2[addr_type], + "interface": intf_r3_r23, + "nexthop_vrf": "BLUE_A", + "vrf": "RED_A", + }, + { + "network": LOOPBACK_1[addr_type], + "interface": intf_r3_r21, + "nexthop_vrf": "RED_A", + "vrf": "BLUE_A", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_route_map_within_vrf_to_alter_bgp_attribute_nexthop_p0(request): + """ + FUNC_12_a: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise a set of BGP prefixes(IPv4+IPv6) from RED_1 and" + " RED_2 in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and" + "BLUE_2 in vrf instances(BLUE_A and BLUE_B)" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that within vrf instances, BGP best path selection" + " algorithm remains intact and doesn't affect any other VRFs" + " routing decision." + ) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Delete nexthop-self configure from r1") + + input_dict_4 = { + "r1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"next_hop_self": False} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {"next_hop_self": False} + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link2": {"next_hop_self": False} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link2": {"next_hop_self": False} + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link3": {"next_hop_self": False} + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link3": {"next_hop_self": False} + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link4": {"next_hop_self": False} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link4": {"next_hop_self": False} + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that within vrf instances, BGP best path selection" + " algorithm remains intact and doesn't affect any other VRFs" + " routing decision." + ) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Expected Behaviour: Routes are rejected because" + " nexthop-self config is deleted \n Error {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Expected Behaviour: Routes are rejected because" + " nexthop-self config is deleted \n Error {}".format(tc_name, result) + + write_test_footer(tc_name) + + +@pytest.mark.parametrize("attribute", ["locPrf", "weight", "metric"]) +def test_route_map_within_vrf_to_alter_bgp_attribute_p0(request, attribute): + """ + FUNC_12_b/c/d: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise a set of BGP prefixes(IPv4+IPv6) from RED_1 and" + " RED_2 in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + }, + "red2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + }, + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and" + "BLUE_2 in vrf instances(BLUE_A and BLUE_B)" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + }, + "blue2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + }, + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "red2", "blue1", "blue2"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure a route-maps to influence BGP parameters - " " Local Preference") + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "route_maps": { + "rmap_r1_{}".format(addr_type): [ + {"action": "permit", "set": {attribute: 120}} + ], + "rmap_r3_{}".format(addr_type): [ + {"action": "permit", "set": {attribute: 150}} + ], + } + } + } + + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure neighbor for route map") + input_dict_4 = { + "r2": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r3": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_r3_ipv4", + "direction": "in", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r3": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_r3_ipv6", + "direction": "in", + } + ] + } + } + }, + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r3": { + "dest_link": { + "r2-link2": { + "route_maps": [ + { + "name": "rmap_r3_ipv4", + "direction": "in", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r3": { + "dest_link": { + "r2-link2": { + "route_maps": [ + { + "name": "rmap_r3_ipv6", + "direction": "in", + } + ] + } + } + }, + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r3": { + "dest_link": { + "r2-link3": { + "route_maps": [ + { + "name": "rmap_r3_ipv4", + "direction": "in", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r3": { + "dest_link": { + "r2-link3": { + "route_maps": [ + { + "name": "rmap_r3_ipv6", + "direction": "in", + } + ] + } + } + }, + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r3": { + "dest_link": { + "r2-link4": { + "route_maps": [ + { + "name": "rmap_r3_ipv4", + "direction": "in", + } + ] + } + } + }, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r3": { + "dest_link": { + "r2-link4": { + "route_maps": [ + { + "name": "rmap_r3_ipv6", + "direction": "in", + } + ] + } + } + }, + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that within vrf instances, BGP best path selection" + " algorithm remains intact and doesn't affect any other VRFs" + " routing decision." + ) + + dut = "r2" + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, input_dict_1, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, input_dict_2, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_route_map_within_vrf_to_alter_bgp_attribute_aspath_p0(request): + """ + FUNC_12_e: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise a set of BGP prefixes(IPv4+IPv6) from RED_1 and" + " RED_2 in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + }, + "red2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + }, + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and" + "BLUE_2 in vrf instances(BLUE_A and BLUE_B)" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + }, + "blue2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + }, + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "red2", "blue1", "blue2"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure a route-maps to influence BGP parameters - " " Local Preference") + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r2": { + "route_maps": { + "rmap_r1_{}".format(addr_type): [ + { + "action": "permit", + "set": { + "path": {"as_num": "111 222", "as_action": "prepend"} + }, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure neighbor for route map") + input_dict_4 = { + "r2": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r3": {"dest_link": {"r2-link1": {}}}, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r3": {"dest_link": {"r2-link1": {}}}, + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r3": {"dest_link": {"r2-link2": {}}}, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r3": {"dest_link": {"r2-link2": {}}}, + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r3": {"dest_link": {"r2-link3": {}}}, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r3": {"dest_link": {"r2-link3": {}}}, + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + }, + "r3": {"dest_link": {"r2-link4": {}}}, + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + }, + "r3": {"dest_link": {"r2-link4": {}}}, + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that within vrf instances, BGP best path selection" + " algorithm remains intact and doesn't affect any other VRFs" + " routing decision." + ) + + dut = "r2" + attribute = "path" + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, input_dict_1, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_best_path_as_per_bgp_attribute( + tgen, addr_type, dut, input_dict_2, attribute + ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_route_map_within_vrf_to_alter_bgp_attribute_lcomm_p0(request): + """ + FUNC_12_f: + Configure route-maps within a VRF, to alter BGP attributes. + Verify that route-map doesn't affect any other VRF instances' + routing on DUT. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise a set of BGP prefixes(IPv4+IPv6) from RED_1 and" + " RED_2 in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + }, + "red2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + }, + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and" + "BLUE_2 in vrf instances(BLUE_A and BLUE_B)" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + }, + "blue2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + }, + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "red2", "blue1", "blue2"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure a route-maps to influence BGP parameters - " " Large-community") + + step("Create standard large commumity-list in r2") + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r2": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "rmap_lcomm_{}".format(addr_type), + "value": "1:1:1 1:2:3 2:1:1 2:2:2", + "large": True, + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Create route-maps in red1 and r1") + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "red1": { + "route_maps": { + "rmap_red1_{}".format(addr_type): [ + { + "action": "permit", + "set": { + "large_community": {"num": "1:1:1 1:2:3 2:1:1 2:2:2"} + }, + } + ] + } + }, + "r2": { + "route_maps": { + "rmap_r1_{}".format(addr_type): [ + { + "action": "permit", + "match": { + "large_community_list": { + "id": "rmap_lcomm_" + addr_type + } + }, + } + ] + } + }, + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure neighbor for route map in red1") + + input_dict_4 = { + "red1": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "route_maps": [ + { + "name": "rmap_red1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "route_maps": [ + { + "name": "rmap_red1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "route_maps": [ + { + "name": "rmap_red1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "route_maps": [ + { + "name": "rmap_red1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure neighbor for route map in r2") + + input_dict_4 = { + "r2": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "route_maps": [ + { + "name": "rmap_r1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "route_maps": [ + { + "name": "rmap_r1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "All the prefixes advertised from RED_1 and BLUE_1 should carry" + " attributes set by outbound route-maps within specific vrfs. " + "Router R1 should be able to match and permit/deny those " + "prefixes based on received attributes. Please use below " + "commands to verify." + ) + + input_dict = { + "largeCommunity": "1:1:1 1:2:3 2:1:1 2:2:2", + } + + for addr_type in ADDR_TYPES: + vrf = "RED_A" + routes = [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]] + result = verify_bgp_community(tgen, addr_type, "r2", routes, input_dict, vrf) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + vrf = "RED_B" + routes = [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]] + result = verify_bgp_community(tgen, addr_type, "r2", routes, input_dict, vrf) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_route_map_match_traffic_based_on_vrf_p0(request): + """ + FUNC_13: + Configure a route-map on DUT to match traffic based + on a VRF interfaces. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from RED_1 " + "in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from from BLUE_1 in" + " vrf instances(BLUE_A and BLUE_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Configure a route-map on R1 to match the prefixes " + "coming from vrf RED_A and set as-prepend to these routes." + ) + + input_dict_4 = { + "r1": { + "route_maps": { + "ABC": [ + { + "action": "permit", + "match": {"source-vrf": "RED_A"}, + "set": {"path": {"as_num": 1, "as_action": "prepend"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "On R1, import the routes form vrf RED_A and RED_B to BLUE_A and" + " apply the route-map under vrf BLUE_A while importing" + ) + + raw_config = { + "r1": { + "raw_config": [ + "router bgp 100 vrf BLUE_A", + "address-family ipv4 unicast", + "import vrf RED_A", + "import vrf RED_B", + "import vrf route-map ABC", + "address-family ipv6 unicast", + "import vrf RED_A", + "import vrf RED_B", + "import vrf route-map ABC", + ] + } + } + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "All the prefixes advertised from RED_1 and BLUE_1 in vrfs " + "RED_B and BLUE_B must prepend the AS number in as-path on R2." + ) + + for addr_type in ADDR_TYPES: + input_dict_7 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_7) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_vrf_lite_with_static_bgp_originated_routes_p0(request): + """ + FUNC_14: + Test VRF-lite with Static+BGP originated routes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from from RED_1" + " in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from from BLUE_1 in" + " vrf instances(BLUE_A and BLUE_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_3 = { + "red1": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK5_1["ipv4"]] + + [NETWORK5_2["ipv4"]] + } + ], + "redistribute": [{"redist_type": "static"}], + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK5_1["ipv6"]] + + [NETWORK5_2["ipv6"]] + } + ], + "redistribute": [{"redist_type": "static"}], + } + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK6_1["ipv4"]] + + [NETWORK6_2["ipv4"]] + } + ], + "redistribute": [{"redist_type": "static"}], + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK6_1["ipv6"]] + + [NETWORK6_2["ipv6"]] + } + ], + "redistribute": [{"redist_type": "static"}], + } + }, + }, + }, + ] + }, + "blue1": { + "bgp": [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK7_1["ipv4"]] + + [NETWORK7_2["ipv4"]] + } + ], + "redistribute": [{"redist_type": "static"}], + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK7_1["ipv6"]] + + [NETWORK7_2["ipv6"]] + } + ], + "redistribute": [{"redist_type": "static"}], + } + }, + }, + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK8_1["ipv4"]] + + [NETWORK8_2["ipv4"]] + } + ], + "redistribute": [{"redist_type": "static"}], + } + }, + "ipv6": { + "unicast": { + "advertise_networks": [ + { + "network": [NETWORK8_1["ipv6"]] + + [NETWORK8_2["ipv6"]] + } + ], + "redistribute": [{"redist_type": "static"}], + } + }, + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Static routes must be installed in associated VRF" " table only.") + + for addr_type in ADDR_TYPES: + dut = "r1" + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "All the routers must receive advertised as well as " + "redistributed(static) prefixes in associated VRF tables." + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_prefix_list_to_permit_deny_prefixes_p0(request): + """ + FUNC_15: + Configure prefix-lists on DUT and apply to BGP peers to + permit/deny prefixes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from from RED_1" + " in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from from BLUE_1 in" + " vrf instances(BLUE_A and BLUE_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify routes are present before applying prefix-list") + for addr_type in ADDR_TYPES: + dut = "r1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "On routers RED_1 and BLUE_1, configure prefix-lists to permit" + " 4 prefixes and deny 1 prefix x.x.x.5. Apply these in outbound" + "direction for each neighbour." + ) + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "red1": { + "prefix_lists": { + addr_type: { + "pflist_red1_{}".format(addr_type): [ + { + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit", + }, + { + "seqid": 11, + "network": NETWORK2_1[addr_type], + "action": "permit", + }, + { + "seqid": 12, + "network": NETWORK1_2[addr_type], + "action": "deny", + }, + { + "seqid": 13, + "network": NETWORK2_2[addr_type], + "action": "deny", + }, + ] + } + } + }, + "blue1": { + "prefix_lists": { + addr_type: { + "pflist_blue1_{}".format(addr_type): [ + { + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit", + }, + { + "seqid": 11, + "network": NETWORK2_1[addr_type], + "action": "permit", + }, + { + "seqid": 12, + "network": NETWORK1_2[addr_type], + "action": "deny", + }, + { + "seqid": 13, + "network": NETWORK2_2[addr_type], + "action": "deny", + }, + ] + } + } + }, + "r1": { + "prefix_lists": { + addr_type: { + "pflist_r1_{}".format(addr_type): [ + { + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit", + }, + { + "seqid": 11, + "network": NETWORK2_1[addr_type], + "action": "deny", + }, + ] + } + } + }, + } + result = create_prefix_lists(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_5 = { + "red1": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "prefix_lists": [ + { + "name": "pflist_red1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "prefix_lists": [ + { + "name": "pflist_red1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "prefix_lists": [ + { + "name": "pflist_red1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "prefix_lists": [ + { + "name": "pflist_red1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + ] + }, + "blue1": { + "bgp": [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link1": { + "prefix_lists": [ + { + "name": "pflist_blue1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link1": { + "prefix_lists": [ + { + "name": "pflist_blue1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link2": { + "prefix_lists": [ + { + "name": "pflist_blue1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link2": { + "prefix_lists": [ + { + "name": "pflist_blue1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that within vrf instances, each BGP neighbor receives 1" + " prefixes in routing table and drops (x.x.x.2)." + ) + + for addr_type in ADDR_TYPES: + dut = "r1" + permitted_routes = { + "red1": { + "static_routes": [ + {"network": [NETWORK1_1[addr_type]], "vrf": "RED_A"}, + {"network": [NETWORK2_1[addr_type]], "vrf": "RED_B"}, + ] + } + } + + denied_routes = { + "red1": { + "static_routes": [ + {"network": [NETWORK1_2[addr_type]], "vrf": "RED_A"}, + {"network": [NETWORK2_2[addr_type]], "vrf": "RED_B"}, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, permitted_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, denied_routes, expected=False) + assert result is not True, "Testcase {} : Failed \n" + "Expected behaviour: Routes are denied by prefix-list \n" + "Error {}".format(tc_name, result) + + step( + "On router R1, configure prefix-lists to permit 2 " + "prefixes(x.x.x.1-2) and deny 2 prefix(x.x.x.3-4). Apply" + " these in inbound direction for each neighbour." + ) + + input_dict_6 = { + "r1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": { + "prefix_lists": [ + { + "name": "pflist_r1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": { + "prefix_lists": [ + { + "name": "pflist_r1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": { + "prefix_lists": [ + { + "name": "pflist_r1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": { + "prefix_lists": [ + { + "name": "pflist_r1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": { + "prefix_lists": [ + { + "name": "pflist_r1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": { + "prefix_lists": [ + { + "name": "pflist_r1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": { + "prefix_lists": [ + { + "name": "pflist_r1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": { + "prefix_lists": [ + { + "name": "pflist_r1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that within vrf instances, each BGP neighbor installs" + " only 1 prefix (x.x.x.1)." + ) + for addr_type in ADDR_TYPES: + dut = "r2" + permitted_routes = { + "red1": { + "static_routes": [{"network": [NETWORK1_1[addr_type]], "vrf": "RED_A"}] + } + } + + denied_routes = { + "red1": { + "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "RED_A"}] + } + } + + result = verify_rib(tgen, addr_type, dut, permitted_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, denied_routes, expected=False) + assert result is not True, "Testcase {} : Failed \n" + "Expected behaviour: Routes are denied by prefix-list \n" + "Error {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_route_map_set_and_match_tag_p0(request): + """ + FUNC_16_1: + Configure a route-map on DUT to match traffic based various + match/set causes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from RED_1" + " in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "tag": 4001, + "vrf": "RED_A", + }, + { + "network": [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and" + "BLUE_2 in vrf instances(BLUE_A and BLUE_B)" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "tag": 4001, + "vrf": "BLUE_A", + }, + { + "network": [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure a route-maps to match tag") + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "red1": { + "route_maps": { + "rmap1_{}".format(addr_type): [ + {"action": "permit", "match": {addr_type: {"tag": "4001"}}} + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure neighbor for route map") + input_dict_4 = { + "red1": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that within vrf instances, BGP best path selection" + " algorithm remains intact and doesn't affect any other VRFs" + " routing decision." + ) + + dut = "r1" + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "tag": 4001, + "vrf": "RED_A", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Expected Behavior: Routes are denied \n" + "Error {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_route_map_set_and_match_metric_p0(request): + """ + FUNC_16_2: + Configure a route-map on DUT to match traffic based various + match/set causes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from RED_1" + " in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and" + "BLUE_2 in vrf instances(BLUE_A and BLUE_B)" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = { + "red1": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"metric": 123}, + } + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"metric": 123}, + } + ] + } + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + }, + ] + }, + "blue1": { + "bgp": [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"metric": 123}, + } + ] + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + { + "redist_type": "static", + "attribute": {"metric": 123}, + } + ] + } + }, + }, + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure a route-maps to match tag") + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "route_maps": { + "rmap1_{}".format(addr_type): [ + {"action": "permit", "match": {"metric": 123}} + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure neighbor for route map") + input_dict_4 = { + "r1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that within vrf instances, BGP best path selection" + " algorithm remains intact and doesn't affect any other VRFs" + " routing decision." + ) + + dut = "r1" + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Expected Behavior: Routes are denied \n" + "Error {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_route_map_set_and_match_community_p0(request): + """ + FUNC_16_3: + Configure a route-map on DUT to match traffic based various + match/set causes. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Advertise unique BGP prefixes(IPv4+IPv6) from RED_1" + " in vrf instances(RED_A and RED_B)." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and" + "BLUE_2 in vrf instances(BLUE_A and BLUE_B)" + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create community-list") + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "standard", + "action": "permit", + "name": "rmap_lcomm_{}".format(addr_type), + "value": "1:1 1:2 1:3 1:4 1:5", + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure a route-maps to match tag") + + step("Create route-maps in red1 and r1") + + for addr_type in ADDR_TYPES: + input_dict_4 = { + "red1": { + "route_maps": { + "rmap_red1_{}".format(addr_type): [ + { + "action": "permit", + "set": {"community": {"num": "1:1 1:2 1:3 1:4 1:5"}}, + } + ] + } + }, + "r1": { + "route_maps": { + "rmap1_{}".format(addr_type): [ + { + "action": "permit", + "match": { + "community_list": {"id": "rmap_lcomm_" + addr_type} + }, + } + ] + } + }, + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure neighbor for route map") + input_dict_4 = { + "red1": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "route_maps": [ + { + "name": "rmap_red1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": { + "route_maps": [ + { + "name": "rmap_red1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "route_maps": [ + { + "name": "rmap_red1_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": { + "route_maps": [ + { + "name": "rmap_red1_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + }, + }, + ] + }, + "r1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv4", + "direction": "in", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": { + "route_maps": [ + { + "name": "rmap1_ipv6", + "direction": "in", + } + ] + } + } + } + } + } + }, + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "All the prefixes advertised from RED_1 and BLUE_1 should carry" + " attributes set by outbound route-maps within specific vrfs. " + "Router R1 should be able to match and permit/deny those " + "prefixes based on received attributes. Please use below " + "commands to verify." + ) + + input_dict = { + "community": "1:1 1:2 1:3 1:4 1:5", + } + + for addr_type in ADDR_TYPES: + vrf = "RED_A" + routes = [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]] + result = verify_bgp_community(tgen, addr_type, "r1", routes, input_dict, vrf) + assert result is True, "Test case {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + vrf = "RED_B" + routes = [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]] + result = verify_bgp_community(tgen, addr_type, "r1", routes, input_dict, vrf) + assert result is True, "Test case {} : 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_multi_vrf_topo2/bgp_multi_vrf_topo2.json b/tests/topotests/bgp_multi_vrf_topo2/bgp_multi_vrf_topo2.json new file mode 100644 index 0000000000..ab570fcc16 --- /dev/null +++ b/tests/topotests/bgp_multi_vrf_topo2/bgp_multi_vrf_topo2.json @@ -0,0 +1,1277 @@ +{ + "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": { + "red1": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "red1-link2": {} + } + } + } + } + } + } + } + ] + }, + "blue1": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "BLUE_A", + "id": "1" + }, + { + "name": "BLUE_B", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "blue1-link2": {} + } + } + } + } + } + } + } + ] + }, + "r1": { + "links": { + "red1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "red1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "blue1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "blue1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r2-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r4-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r4-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + }, + { + "name": "BLUE_A", + "id": "3" + }, + { + "name": "BLUE_B", + "id": "4" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": {} + } + }, + "r2": { + "dest_link": { + "r1-link1": + { "next_hop_self": true } + } + }, + "r4": { + "dest_link": { + "r1-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link1": {} + } + }, + "r2": { + "dest_link": { + "r1-link1": + { "next_hop_self": true } + } + }, + "r4": { + "dest_link": { + "r1-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": {} + } + }, + "r2": { + "dest_link": { + "r1-link2": + { "next_hop_self": true } + } + }, + "r4": { + "dest_link": { + "r1-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "red1": { + "dest_link": { + "r1-link2": {} + } + }, + "r2": { + "dest_link": { + "r1-link2": + { "next_hop_self": true } + } + }, + "r4": { + "dest_link": { + "r1-link2": {} + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": {} + } + }, + "r2": { + "dest_link": { + "r1-link3": + { "next_hop_self": true } + } + }, + "r4": { + "dest_link": { + "r1-link3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link1": {} + } + }, + "r2": { + "dest_link": { + "r1-link3": + { "next_hop_self": true } + } + }, + "r4": { + "dest_link": { + "r1-link3": {} + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": {} + } + }, + "r2": { + "dest_link": { + "r1-link4": + { "next_hop_self": true } + } + }, + "r4": { + "dest_link": { + "r1-link4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "blue1": { + "dest_link": { + "r1-link2": {} + } + }, + "r2": { + "dest_link": { + "r1-link4": + { "next_hop_self": true } + } + }, + "r4": { + "dest_link": { + "r1-link4": {} + } + } + } + } + } + } + } + ] + }, + "r2": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r1-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r1-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r3-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + }, + { + "name": "BLUE_A", + "id": "3" + }, + { + "name": "BLUE_B", + "id": "4" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {} + } + }, + "r3": { + "dest_link": { + "r2-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": {} + } + }, + "r3": { + "dest_link": { + "r2-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": {} + } + }, + "r3": { + "dest_link": { + "r2-link2": {} + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": {} + } + }, + "r3": { + "dest_link": { + "r2-link3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": {} + } + }, + "r3": { + "dest_link": { + "r2-link3": {} + } + } + } + } + } + } + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": {} + } + }, + "r3": { + "dest_link": { + "r2-link4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": {} + } + }, + "r3": { + "dest_link": { + "r2-link4": {} + } + } + } + } + } + } + } + ] + }, + "r3": { + "links": { + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r2-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r4-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r4-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "red2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "red2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "blue2-link1": {"ipv4": "auto", "ipv6": "autor3", "vrf": "BLUE_A"}, + "blue2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + }, + { + "name": "BLUE_A", + "id": "3" + }, + { + "name": "BLUE_B", + "id": "4" + } + ], + "bgp": + [ + { + "local_as": "200", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": {} + } + }, + "r4": { + "dest_link": { + "r3-link1": {} + } + }, + "red2": { + "dest_link": { + "r3-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "red2": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link2": {} + } + }, + "r4": { + "dest_link": { + "r3-link2": {} + } + }, + "red2": { + "dest_link": { + "r3-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r3-link2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "red2": { + "dest_link": { + "r3-link2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link3": {} + } + }, + "r4": { + "dest_link": { + "r3-link3": {} + } + }, + "blue2": { + "dest_link": { + "r3-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link3": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r3-link3": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "blue2": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "200", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link4": {} + } + }, + "r4": { + "dest_link": { + "r3-link4": {} + } + }, + "blue2": { + "dest_link": { + "r3-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link4": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r3-link4": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "blue2": { + "dest_link": { + "r3-link2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r4": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r1-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r1-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r3-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + }, + { + "name": "BLUE_A", + "id": "3" + }, + { + "name": "BLUE_B", + "id": "4" + } + ], + "bgp": + [ + { + "local_as": "400", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": {} + } + }, + "r3": { + "dest_link": { + "r4-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r3": { + "dest_link": { + "r4-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "400", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link2": {} + } + }, + "r3": { + "dest_link": { + "r4-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r3": { + "dest_link": { + "r4-link2": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "400", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link3": {} + } + }, + "r3": { + "dest_link": { + "r4-link3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link3": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r3": { + "dest_link": { + "r4-link3": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "400", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link4": {} + } + }, + "r3": { + "dest_link": { + "r4-link4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link4": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + }, + "r3": { + "dest_link": { + "r4-link4": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "red2": { + "links": { + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"} + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "500", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "red2-link2": {} + } + } + } + } + } + } + } + ] + }, + "blue2": { + "links": { + "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"} + }, + "vrfs":[ + { + "name": "BLUE_A", + "id": "1" + }, + { + "name": "BLUE_B", + "id": "2" + } + ], + "bgp": + [ + { + "local_as": "800", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link1": {} + } + } + } + } + } + } + }, + { + "local_as": "800", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "blue2-link2": {} + } + } + } + } + } + } + } + ] + } + } +} diff --git a/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py b/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py new file mode 100755 index 0000000000..27c4430a52 --- /dev/null +++ b/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py @@ -0,0 +1,2012 @@ +#!/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: + +CHAOS_1: + Do a shut and no shut on connecting interface of DUT, + to see if all vrf instances clear their respective BGP tables + during the interface down and restores when interface brought +kCHAOS_3: + VRF leaking - next-hop interface is flapping. +CHAOS_5: + VRF - VLANs - Routing Table ID - combination testcase + on DUT. +CHAOS_9: + Verify that all vrf instances fall back + to backup path, if primary link goes down. + +""" + +import os +import sys +import json +import time +import pytest +from copy import deepcopy +from time import sleep + + +# 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 mininet.topo import Topo + +from lib.common_config import ( + step, + verify_rib, + start_topology, + write_test_header, + check_address_types, + write_test_footer, + reset_config_on_routers, + create_route_maps, + shutdown_bringup_interface, + start_router_daemons, + kill_router_daemons, + create_static_routes, + create_vrf_cfg, + create_interfaces_cfg, + create_interface_in_kernel, + kill_mininet_routers_process, + get_frr_ipv6_linklocal, + check_router_status, + apply_raw_config, +) + +from lib.topolog import logger +from lib.bgp import clear_bgp, verify_bgp_rib, create_router_bgp, verify_bgp_convergence +from lib.topojson import build_config_from_json, build_topo_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/bgp_multi_vrf_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": "1.1.1.1/32", "ipv6": "1::1/128"} +NETWORK1_2 = {"ipv4": "1.1.1.2/32", "ipv6": "1::2/128"} +NETWORK2_1 = {"ipv4": "2.1.1.1/32", "ipv6": "2::1/128"} +NETWORK2_2 = {"ipv4": "2.1.1.2/32", "ipv6": "2::2/128"} +NETWORK3_1 = {"ipv4": "3.1.1.1/32", "ipv6": "3::1/128"} +NETWORK3_2 = {"ipv4": "3.1.1.2/32", "ipv6": "3::2/128"} +NETWORK4_1 = {"ipv4": "4.1.1.1/32", "ipv6": "4::1/128"} +NETWORK4_2 = {"ipv4": "4.1.1.2/32", "ipv6": "4::2/128"} +NETWORK9_1 = {"ipv4": "100.1.0.1/30", "ipv6": "100::1/126"} +NETWORK9_2 = {"ipv4": "100.1.0.2/30", "ipv6": "100::2/126"} + +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + +LOOPBACK_2 = { + "ipv4": "20.20.20.20/32", + "ipv6": "20::20:20/128", + "ipv4_mask": "255.255.255.255", + "ipv6_mask": None, +} + +MAX_PATHS = 2 +KEEPALIVETIMER = 1 +HOLDDOWNTIMER = 3 +PREFERRED_NEXT_HOP = "link_local" + + +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 + """ + + 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. + + # Kill stale mininet routers and process + kill_mininet_routers_process(tgen) + + # 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) + + 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_vrf_with_multiple_links_p1(request): + """ + CHAOS_9: + Verify that all vrf instances fall back + to backup path, if primary link goes down. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Configure BGP neighborships(IPv4+IPv6) between R1 and R4 " + "using exact same link IPs for all 4 VRFs." + ) + + topo_modify = deepcopy(topo) + build_config_from_json(tgen, topo_modify) + + interfaces = ["link1", "link2", "link3", "link4"] + for interface in interfaces: + topo_modify["routers"]["r1"]["links"]["r4-{}".format(interface)][ + "delete" + ] = True + topo_modify["routers"]["r4"]["links"]["r1-{}".format(interface)][ + "delete" + ] = True + + step("Build interface config from json") + create_interfaces_cfg(tgen, topo_modify["routers"]) + + interfaces = ["link1", "link2", "link3", "link4"] + for interface in interfaces: + del topo_modify["routers"]["r1"]["links"]["r4-{}".format(interface)]["delete"] + del topo_modify["routers"]["r4"]["links"]["r1-{}".format(interface)]["delete"] + + r1_config = [] + r4_config = [] + for addr_type in ADDR_TYPES: + interfaces = ["link1", "link2", "link3", "link4"] + for interface in interfaces: + intf_name_r1 = topo_modify["routers"]["r1"]["links"][ + "r4-{}".format(interface) + ]["interface"] + topo_modify["routers"]["r1"]["links"]["r4-{}".format(interface)][ + addr_type + ] = NETWORK9_1[addr_type] + + intf_name_r4 = topo_modify["routers"]["r4"]["links"][ + "r1-{}".format(interface) + ]["interface"] + topo_modify["routers"]["r4"]["links"]["r1-{}".format(interface)][ + addr_type + ] = NETWORK9_2[addr_type] + + r1_config.append("interface {}".format(intf_name_r1)) + r4_config.append("interface {}".format(intf_name_r4)) + if addr_type == "ipv4": + r1_config.append("no ip address {}".format(NETWORK9_1[addr_type])) + r4_config.append("no ip address {}".format(NETWORK9_2[addr_type])) + else: + r1_config.append("no ipv6 address {}".format(NETWORK9_1[addr_type])) + r4_config.append("no ipv6 address {}".format(NETWORK9_2[addr_type])) + + step("Build interface config from json") + create_interfaces_cfg(tgen, topo_modify["routers"]) + + step("Create bgp config") + result = create_router_bgp(tgen, topo_modify["routers"]) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify BGP convergence") + + result = verify_bgp_convergence(tgen, topo_modify) + assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result) + + step( + "Advertise below prefixes in BGP using static redistribution" + " for both vrfs (RED_A and BLUE_A) on router R2.." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["r1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + VRFS = ["RED_A", "RED_B", "BLUE_A", "BLUE_B"] + AS_NUM = [100, 100, 100, 100] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo_modify, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify routes are installed with same nexthop in different" " VRFs") + + for addr_type in ADDR_TYPES: + dut = "r4" + _input_dict = { + "r1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + + R1_NEXTHOP = topo_modify["routers"]["r1"]["links"]["r4-link1"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, _input_dict, next_hop=R1_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + _input_dict = { + "r1": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + } + ] + } + } + + R1_NEXTHOP = topo_modify["routers"]["r1"]["links"]["r4-link1"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, _input_dict, next_hop=R1_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + _input_dict = { + "r1": { + "static_routes": [ + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + } + ] + } + } + + R1_NEXTHOP = topo_modify["routers"]["r1"]["links"]["r4-link1"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, _input_dict, next_hop=R1_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + _input_dict = { + "r1": { + "static_routes": [ + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + } + ] + } + } + + R1_NEXTHOP = topo_modify["routers"]["r1"]["links"]["r4-link1"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, _input_dict, next_hop=R1_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Configure a route-map on R3 to prepend as-path and apply" + " for neighbour router R2 in both vrfs, in inbound direction." + ) + + input_dict_4 = { + "r3": { + "route_maps": { + "ASP": [ + { + "action": "permit", + "set": {"path": {"as_num": 123, "as_action": "prepend"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Apply route-map to neighbours") + step( + "Configure ECMP on router R3 using 'max-path' command for both" + " VRFs RED_A and BLUE_A." + ) + + input_dict_5 = { + "r3": { + "bgp": [ + { + "local_as": "200", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "route_maps": [ + {"name": "ASP", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "route_maps": [ + {"name": "ASP", "direction": "in"} + ] + } + } + }, + "r4": { + "dest_link": { + "r3-link1": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + } + ] + } + } + }, + } + } + }, + }, + }, + { + "local_as": "200", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link2": { + "route_maps": [ + {"name": "ASP", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link2": { + "route_maps": [ + {"name": "ASP", "direction": "in"} + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "200", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link3": { + "route_maps": [ + {"name": "ASP", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link3": { + "route_maps": [ + {"name": "ASP", "direction": "in"} + ] + } + } + }, + "r4": { + "dest_link": { + "r3-link3": { + "route_maps": [ + { + "name": "rmap_global", + "direction": "in", + } + ] + } + } + }, + } + } + }, + }, + }, + { + "local_as": "200", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link4": { + "route_maps": [ + {"name": "ASP", "direction": "in"} + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link4": { + "route_maps": [ + {"name": "ASP", "direction": "in"} + ] + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo_modify, input_dict_5) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r3" + peer = "r2" + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "vrf": "RED_A", + } + ] + } + } + + intf = topo_modify["routers"][peer]["links"]["r3-link1"]["interface"] + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + R2_NEXTHOP = get_frr_ipv6_linklocal(tgen, peer, intf=intf, vrf="RED_A") + else: + R2_NEXTHOP = topo_modify["routers"]["r2"]["links"]["r3-link1"][ + addr_type + ].split("/")[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R2_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "vrf": "BLUE_A", + } + ] + } + } + + intf = topo["routers"][peer]["links"]["r3-link3"]["interface"] + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + R2_NEXTHOP = get_frr_ipv6_linklocal(tgen, peer, intf=intf, vrf="BLUE_A") + else: + R2_NEXTHOP = topo_modify["routers"]["r2"]["links"]["r3-link3"][ + addr_type + ].split("/")[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R2_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Configure ECMP on router R3 using max-path command for" + " both VRFs RED_A and BLUE_A." + ) + + input_dict_7 = { + "r3": { + "bgp": [ + { + "local_as": "200", + "vrf": "RED_A", + "address_family": { + "ipv4": {"unicast": {"maximum_paths": {"ebgp": MAX_PATHS,}}}, + "ipv6": {"unicast": {"maximum_paths": {"ebgp": MAX_PATHS,}}}, + }, + }, + { + "local_as": "200", + "vrf": "BLUE_A", + "address_family": { + "ipv4": {"unicast": {"maximum_paths": {"ebgp": MAX_PATHS,}}}, + "ipv6": {"unicast": {"maximum_paths": {"ebgp": MAX_PATHS,}}}, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo_modify, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("R3 should install prefixes from both next-hops (R2 and R4)") + + for addr_type in ADDR_TYPES: + dut = "r3" + peer = "r2" + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "vrf": "RED_A", + } + ] + } + } + + intf = topo_modify["routers"][peer]["links"]["r3-link1"]["interface"] + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + R2_NEXTHOP = get_frr_ipv6_linklocal(tgen, peer, intf=intf, vrf="RED_A") + else: + R2_NEXTHOP = topo_modify["routers"]["r2"]["links"]["r3-link1"][ + addr_type + ].split("/")[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R2_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "vrf": "BLUE_A", + } + ] + } + } + + intf = topo_modify["routers"][peer]["links"]["r3-link3"]["interface"] + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + R2_NEXTHOP = get_frr_ipv6_linklocal(tgen, peer, intf=intf, vrf="BLUE_A") + else: + R2_NEXTHOP = topo_modify["routers"]["r2"]["links"]["r3-link3"][ + addr_type + ].split("/")[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R2_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Shutdown interface between R2 and R3 for vrfs RED_A and " "BLUE_A.") + + intf1 = topo_modify["routers"]["r2"]["links"]["r3-link1"]["interface"] + intf2 = topo_modify["routers"]["r2"]["links"]["r3-link3"]["interface"] + + interfaces = [intf1, intf2] + for intf in interfaces: + shutdown_bringup_interface(tgen, "r2", intf, False) + + for addr_type in ADDR_TYPES: + dut = "r3" + peer = "r4" + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "vrf": "RED_A", + } + ] + } + } + + R4_NEXTHOP = topo_modify["routers"]["r4"]["links"]["r3-link1"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R4_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "vrf": "BLUE_A", + } + ] + } + } + + R4_NEXTHOP = topo_modify["routers"]["r4"]["links"]["r3-link3"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R4_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Unshut the interfaces between R2 and R3 for vrfs RED_A and BLUE_A.") + + for intf in interfaces: + shutdown_bringup_interface(tgen, "r2", intf, True) + + for addr_type in ADDR_TYPES: + dut = "r3" + peer = "r2" + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "vrf": "RED_A", + } + ] + } + } + + R4_NEXTHOP = topo_modify["routers"]["r4"]["links"]["r3-link1"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R4_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "vrf": "BLUE_A", + } + ] + } + } + + R4_NEXTHOP = topo_modify["routers"]["r4"]["links"]["r3-link3"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R4_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Remove route-map from R3 for vrfs RED_A and BLUE_A.") + + input_dict_6 = { + "r3": { + "bgp": [ + { + "local_as": "200", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "route_maps": [ + { + "name": "ASP_ipv4", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "route_maps": [ + { + "name": "ASP_ipv6", + "direction": "in", + "delete": True, + }, + { + "name": "rmap_global", + "direction": "in", + }, + ] + } + } + } + } + } + }, + }, + }, + { + "local_as": "200", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link3": { + "route_maps": [ + { + "name": "ASP_ipv4", + "direction": "in", + "delete": True, + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link3": { + "route_maps": [ + { + "name": "ASP_ipv6", + "direction": "in", + "delete": True, + }, + { + "name": "rmap_global", + "direction": "in", + }, + ] + } + } + } + } + } + }, + }, + }, + ] + } + } + + result = create_router_bgp(tgen, topo_modify, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r3" + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "vrf": "RED_A", + } + ] + } + } + + R2_NEXTHOP = topo_modify["routers"]["r2"]["links"]["r3-link1"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R2_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "vrf": "BLUE_A", + } + ] + } + } + + R2_NEXTHOP = topo_modify["routers"]["r2"]["links"]["r3-link3"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R2_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Shutdown links between between R2 and R3 for vrfs RED_A and" " BLUE_A.") + + for intf in interfaces: + shutdown_bringup_interface(tgen, "r2", intf, False) + + for addr_type in ADDR_TYPES: + dut = "r3" + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "vrf": "RED_A", + } + ] + } + } + + R4_NEXTHOP = topo_modify["routers"]["r4"]["links"]["r3-link1"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R4_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + input_dict = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "vrf": "BLUE_A", + } + ] + } + } + + R4_NEXTHOP = topo_modify["routers"]["r4"]["links"]["r3-link3"][addr_type].split( + "/" + )[0] + + result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R4_NEXTHOP) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Bringup links between between R2 and R3 for vrfs RED_A and" " BLUE_A.") + + for intf in interfaces: + shutdown_bringup_interface(tgen, "r2", intf, True) + + step("Deleting manualy assigned ip address from router r1 and r4 interfaces") + raw_config = {"r1": {"raw_config": r1_config}, "r4": {"raw_config": r4_config}} + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_shut_noshut_p1(request): + """ + CHAOS_1: + Do a shut and no shut on connecting interface of DUT, + to see if all vrf instances clear their respective BGP tables + during the interface down and restores when interface brought + back up again. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Build interface config from json") + create_interfaces_cfg(tgen, topo["routers"]) + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Advertise unique prefixes in BGP using static redistribution" + " for both vrfs (RED_A and RED_B) on router RED_1." + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Advertise unique prefixes in BGP using static redistribution" + " for both vrfs (BLUE_A and BLUE_B) on router BLUE_1." + ) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + result = create_static_routes(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = {} + for dut in ["red1", "blue1"]: + temp = {dut: {"bgp": []}} + input_dict_3.update(temp) + + if "red" in dut: + VRFS = ["RED_A", "RED_B"] + AS_NUM = [500, 500] + elif "blue" in dut: + VRFS = ["BLUE_A", "BLUE_B"] + AS_NUM = [800, 800] + + for vrf, as_num in zip(VRFS, AS_NUM): + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ) + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Api call to modfiy BGP timerse") + + input_dict_4 = { + "r1": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link3": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link3": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link4": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link4": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + ] + }, + "r2": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "RED_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link2": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link3": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + { + "local_as": "100", + "vrf": "BLUE_B", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link4": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + }, + ] + }, + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, "r1", vrf=["RED_A", "RED_B", "BLUE_A", "BLUE_B"]) + + clear_bgp(tgen, addr_type, "r2", vrf=["RED_A", "RED_B", "BLUE_A", "BLUE_B"]) + + step("Shut down connecting interface between R1<<>>R2 on R1.") + step("Repeat step-3 and step-4 10 times.") + + for count in range(1, 2): + step("Iteration {}".format(count)) + step("Shut down connecting interface between R1<<>>R2 on R1.") + + intf1 = topo["routers"]["r1"]["links"]["r2-link1"]["interface"] + intf2 = topo["routers"]["r1"]["links"]["r2-link2"]["interface"] + intf3 = topo["routers"]["r1"]["links"]["r2-link3"]["interface"] + intf4 = topo["routers"]["r1"]["links"]["r2-link4"]["interface"] + + interfaces = [intf1, intf2, intf3, intf4] + for intf in interfaces: + shutdown_bringup_interface(tgen, "r1", intf, False) + + step( + "On R2, all BGP peering in respective vrf instances go down" + " when the interface is shut" + ) + + step("Sleeping for holddowntimer+1 sec..") + sleep(HOLDDOWNTIMER + 1) + + result = verify_bgp_convergence(tgen, topo, expected=False) + assert result is not True, "Testcase {} : Failed \n " + "Expected Behaviour: BGP will not be converged \n " + "Error {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert result is not True, "Testcase {} : Failed \n " + " Expected Behaviour: Routes are flushed out \n " + "Error {}".format(tc_name, result) + + result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) + assert result is not True, "Testcase {} : Failed \n " + " Expected Behaviour: Routes are flushed out \n " + "Error {}".format(tc_name, result) + + step("Bring up connecting interface between R1<<>>R2 on R1.") + for intf in interfaces: + shutdown_bringup_interface(tgen, "r1", intf, True) + + step( + "R2 restores BGP peering and routing tables in all vrf " + "instances when interface brought back up again" + ) + + result = verify_bgp_convergence(tgen, topo) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + dut = "r2" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + }, + { + "network": [NETWORK2_1[addr_type]] + + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_B", + }, + ] + } + } + + input_dict_2 = { + "blue1": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_A", + }, + { + "network": [NETWORK2_1[addr_type]] + + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "BLUE_B", + }, + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_vrf_vlan_routing_table_p1(request): + """ + CHAOS_5: + VRF - VLANs - Routing Table ID - combination testcase + on DUT. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step( + "Advertise unique prefixes(IPv4+IPv6) in BGP using" + " network command for vrf RED_A on router R2" + ) + + for addr_type in ADDR_TYPES: + input_dict_1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = { + "r2": { + "bgp": [ + { + "local_as": "100", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that static routes(IPv4+IPv6) is overridden and doesn't" + " have duplicate entries within VRF RED_A on router RED-1" + ) + + for addr_type in ADDR_TYPES: + dut = "r3" + input_dict_1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Api call to modfiy BGP timerse") + + input_dict_4 = { + "r3": { + "bgp": [ + { + "local_as": "200", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": KEEPALIVETIMER, + "holddowntimer": HOLDDOWNTIMER, + } + } + } + } + } + }, + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + clear_bgp(tgen, addr_type, "r3", vrf=["RED_A"]) + + step("Repeat for 5 times.") + + for count in range(1, 2): + step("Iteration {}..".format(count)) + step("Delete a specific VRF instance(RED_A) from router R3") + + input_dict = {"r3": {"vrfs": [{"name": "RED_A", "id": "1", "delete": True}]}} + + result = create_vrf_cfg(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Sleeping for holdowntimer+1 sec..") + sleep(HOLDDOWNTIMER + 1) + + for addr_type in ADDR_TYPES: + dut = "r3" + input_dict_1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed \n Expected Behaviour: Routes are" + " cleaned \n Error {}".format(tc_name, result) + + step("Add/reconfigure the same VRF instance again") + + result = create_vrf_cfg(tgen, {"r3": topo["routers"]["r3"]}) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "After deleting VRFs ipv6 addresses wil be deleted from kernel " + " Adding back ipv6 addresses" + ) + + dut = "r3" + vrf = "RED_A" + + for c_link, c_data in topo["routers"][dut]["links"].items(): + if c_data["vrf"] != vrf: + continue + + intf_name = c_data["interface"] + intf_ipv6 = c_data["ipv6"] + + create_interface_in_kernel( + tgen, dut, intf_name, intf_ipv6, vrf, create=False + ) + + step("Sleeping for holdowntimer+1 sec..") + sleep(HOLDDOWNTIMER + 1) + + for addr_type in ADDR_TYPES: + dut = "r3" + input_dict_1 = { + "r2": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + "vrf": "RED_A", + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_vrf_route_leaking_next_hop_interface_flapping_p1(request): + """ + CHAOS_3: + VRF leaking - next-hop interface is flapping. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + step("Create loopback interface") + + for addr_type in ADDR_TYPES: + create_interface_in_kernel( + tgen, + "red1", + "loopback2", + LOOPBACK_2[addr_type], + "RED_B", + LOOPBACK_2["{}_mask".format(addr_type)], + ) + + intf_red1_r11 = topo["routers"]["red1"]["links"]["r1-link2"]["interface"] + for addr_type in ADDR_TYPES: + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": LOOPBACK_2[addr_type], + "interface": intf_red1_r11, + "nexthop_vrf": "RED_B", + "vrf": "RED_A", + } + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static..") + + input_dict_3 = { + "red1": { + "bgp": [ + { + "local_as": "500", + "vrf": "RED_A", + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "static"}]} + }, + }, + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("VRF RED_A should install a route for vrf RED_B's " "loopback ip.") + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": LOOPBACK_2[addr_type], + "interface": intf_red1_r11, + "nexthop_vrf": "RED_B", + "vrf": "RED_A", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1, protocol="static") + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Repeat step-2 to 4 at least 5 times") + + for count in range(1, 2): + intf1 = topo["routers"]["red1"]["links"]["r1-link2"]["interface"] + + step( + "Iteration {}: Shutdown interface {} on router" + "RED_1.".format(count, intf1) + ) + shutdown_bringup_interface(tgen, "red1", intf1, False) + + step("Verify that RED_A removes static route from routing " "table.") + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": LOOPBACK_2[addr_type], + "interface": intf_red1_r11, + "nexthop_vrf": "RED_B", + "vrf": "RED_A", + } + ] + } + } + + result = verify_rib( + tgen, addr_type, dut, input_dict_1, protocol="static", expected=False + ) + assert result is not True, ( + "Testcase {} : Failed \n Expected Behaviour: Routes are" + " not present Error {}".format(tc_name, result) + ) + + step("Bring up interface {} on router RED_1 again.".format(intf1)) + shutdown_bringup_interface(tgen, "red1", intf1, True) + + step( + "Verify that RED_A reinstalls static route pointing to " + "RED_B's IP in routing table again" + ) + + for addr_type in ADDR_TYPES: + dut = "red1" + input_dict_1 = { + "red1": { + "static_routes": [ + { + "network": LOOPBACK_2[addr_type], + "interface": intf_red1_r11, + "nexthop_vrf": "RED_B", + "vrf": "RED_A", + } + ] + } + } + + result = verify_rib(tgen, addr_type, dut, input_dict_1, protocol="static") + 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/lib/bgp.py b/tests/topotests/lib/bgp.py index 2dd90e9a86..69c807f300 100644 --- a/tests/topotests/lib/bgp.py +++ b/tests/topotests/lib/bgp.py @@ -75,8 +75,12 @@ def create_router_bgp(tgen, topo, input_dict=None, build=False, load_config=True "address_family": { "ipv4": { "unicast": { - "redistribute": [ - {"redist_type": "static"}, + "redistribute": [{ + "redist_type": "static", + "attribute": { + "metric" : 123 + } + }, {"redist_type": "connected"} ], "advertise_networks": [ @@ -143,50 +147,55 @@ def create_router_bgp(tgen, topo, input_dict=None, build=False, load_config=True logger.debug("Router %s: 'bgp' not present in input_dict", router) continue - data_all_bgp = __create_bgp_global(tgen, input_dict, router, build) - if data_all_bgp: - bgp_data = input_dict[router]["bgp"] + bgp_data_list = input_dict[router]["bgp"] - bgp_addr_data = bgp_data.setdefault("address_family", {}) + if type(bgp_data_list) is not list: + bgp_data_list = [bgp_data_list] - if not bgp_addr_data: - logger.debug( - "Router %s: 'address_family' not present in " "input_dict for BGP", - router, - ) - else: + for bgp_data in bgp_data_list: + data_all_bgp = __create_bgp_global(tgen, bgp_data, router, build) + if data_all_bgp: + bgp_addr_data = bgp_data.setdefault("address_family", {}) - ipv4_data = bgp_addr_data.setdefault("ipv4", {}) - ipv6_data = bgp_addr_data.setdefault("ipv6", {}) - - neigh_unicast = ( - True - if ipv4_data.setdefault("unicast", {}) - or ipv6_data.setdefault("unicast", {}) - else False - ) - - if neigh_unicast: - data_all_bgp = __create_bgp_unicast_neighbor( - tgen, - topo, - input_dict, + if not bgp_addr_data: + logger.debug( + "Router %s: 'address_family' not present in " + "input_dict for BGP", router, - afi_test, - config_data=data_all_bgp, + ) + else: + + ipv4_data = bgp_addr_data.setdefault("ipv4", {}) + ipv6_data = bgp_addr_data.setdefault("ipv6", {}) + + neigh_unicast = ( + True + if ipv4_data.setdefault("unicast", {}) + or ipv6_data.setdefault("unicast", {}) + else False ) - try: - result = create_common_configuration( - tgen, router, data_all_bgp, "bgp", build, load_config - ) - except InvalidCLIError: - # Traceback - errormsg = traceback.format_exc() - logger.error(errormsg) - return errormsg + if neigh_unicast: + data_all_bgp = __create_bgp_unicast_neighbor( + tgen, + topo, + bgp_data, + router, + afi_test, + config_data=data_all_bgp, + ) - logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + try: + result = create_common_configuration( + tgen, router, data_all_bgp, "bgp", build, load_config + ) + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: create_router_bgp()") return result @@ -206,19 +215,16 @@ def __create_bgp_global(tgen, input_dict, router, build=False): True or False """ + result = False logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) - bgp_data = input_dict[router]["bgp"] + bgp_data = input_dict del_bgp_action = bgp_data.setdefault("delete", False) - if del_bgp_action: - config_data = ["no router bgp"] - - return config_data config_data = [] if "local_as" not in bgp_data and build: - logger.error( + logger.debug( "Router %s: 'local_as' not present in input_dict" "for BGP", router ) return False @@ -229,6 +235,12 @@ def __create_bgp_global(tgen, input_dict, router, build=False): if vrf_id: cmd = "{} vrf {}".format(cmd, vrf_id) + if del_bgp_action: + cmd = "no {}".format(cmd) + config_data.append(cmd) + + return config_data + config_data.append(cmd) config_data.append("no bgp ebgp-requires-policy") @@ -325,12 +337,15 @@ def __create_bgp_unicast_neighbor( * `build` : Only for initial setup phase this is set as True. """ + result = False logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) add_neigh = True + bgp_data = input_dict if "router bgp" in config_data: add_neigh = False - bgp_data = input_dict[router]["bgp"]["address_family"] + + bgp_data = input_dict["address_family"] for addr_type, addr_dict in bgp_data.iteritems(): if not addr_dict: @@ -403,14 +418,19 @@ def __create_bgp_unicast_neighbor( if redistribute_data: for redistribute in redistribute_data: if "redist_type" not in redistribute: - logger.error( + logger.debug( "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) + if isinstance(redist_attr, dict): + for key, value in redist_attr.items(): + cmd = "{} {} {}".format(cmd, key, value) + else: + cmd = "{} {}".format(cmd, redist_attr) + del_action = redistribute.setdefault("delete", False) if del_action: cmd = "no {}".format(cmd) @@ -453,13 +473,18 @@ def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True): config_data = [] logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) - bgp_data = input_dict[router]["bgp"]["address_family"] + bgp_data = input_dict["address_family"] neigh_data = bgp_data[addr_type]["unicast"]["neighbor"] for name, peer_dict in neigh_data.iteritems(): for dest_link, peer in peer_dict["dest_link"].iteritems(): nh_details = topo[name] - remote_as = nh_details["bgp"]["local_as"] + + if "vrfs" in topo[router]: + remote_as = nh_details["bgp"][0]["local_as"] + else: + remote_as = nh_details["bgp"]["local_as"] + update_source = None if dest_link in nh_details["links"].keys(): @@ -549,7 +574,7 @@ def __create_bgp_unicast_address_family( config_data = [] logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) - bgp_data = input_dict[router]["bgp"]["address_family"] + bgp_data = input_dict["address_family"] neigh_data = bgp_data[addr_type]["unicast"]["neighbor"] for peer_name, peer_dict in deepcopy(neigh_data).iteritems(): @@ -605,8 +630,12 @@ def __create_bgp_unicast_address_family( allowas_in = peer.setdefault("allowas-in", None) # next-hop-self - if next_hop_self: - config_data.append("{} next-hop-self".format(neigh_cxt)) + if next_hop_self is not None: + if next_hop_self is True: + config_data.append("{} next-hop-self".format(neigh_cxt)) + else: + config_data.append("no {} next-hop-self".format(neigh_cxt)) + # send_community if send_community: config_data.append("{} send-community".format(neigh_cxt)) @@ -840,80 +869,291 @@ def verify_router_id(tgen, topo, input_dict): @retry(attempts=20, wait=2, return_is_str=True) -def verify_bgp_convergence(tgen, topo): +def verify_bgp_convergence(tgen, topo, dut=None): """ 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 + * `dut`: device under test + Usage ----- # To veriry is BGP is converged for all the routers used in topology - results = verify_bgp_convergence(tgen, topo, "ipv4") + results = verify_bgp_convergence(tgen, topo, dut="r1") + Returns ------- errormsg(str) or True """ - logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + logger.debug("Entering lib API: verify_bgp_convergence()") for router, rnode in tgen.routers().iteritems(): if "bgp" not in topo["routers"][router]: continue - logger.info("Verifying BGP Convergence on router %s", router) - show_bgp_json = run_frr_cmd(rnode, "show bgp summary json", isjson=True) + if dut is not None and dut != router: + continue + + logger.info("Verifying BGP Convergence on router %s:", router) + show_bgp_json = run_frr_cmd(rnode, "show bgp vrf all summary json", isjson=True) # 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 - 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_data_list = topo["routers"][router]["bgp"] - bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] + if type(bgp_data_list) is not list: + bgp_data_list = [bgp_data_list] - for bgp_neighbor in bgp_neighbors: - total_peer += len(bgp_neighbors[bgp_neighbor]["dest_link"]) + for bgp_data in bgp_data_list: + if "vrf" in bgp_data: + vrf = bgp_data["vrf"] + if vrf is None: + vrf = "default" + else: + vrf = "default" - 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"] + # To find neighbor ip type + bgp_addr_type = bgp_data["address_family"] + if "l2vpn" in bgp_addr_type: + total_evpn_peer = 0 - 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 "neighbor" not in bgp_addr_type["l2vpn"]["evpn"]: + continue - 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 + bgp_neighbors = bgp_addr_type["l2vpn"]["evpn"]["neighbor"] + total_evpn_peer += len(bgp_neighbors) + + no_of_evpn_peer = 0 + for bgp_neighbor, peer_data in bgp_neighbors.items(): + for _addr_type, dest_link_dict in peer_data.items(): + data = topo["routers"][bgp_neighbor]["links"] + for dest_link in dest_link_dict.keys(): + if dest_link in data: + peer_details = peer_data[_addr_type][dest_link] + + neighbor_ip = data[dest_link][_addr_type].split("/")[0] + nh_state = None + + if ( + "ipv4Unicast" in show_bgp_json[vrf] + or "ipv6Unicast" in show_bgp_json[vrf] + ): + errormsg = ( + "[DUT: %s] VRF: %s, " + "ipv4Unicast/ipv6Unicast" + " address-family present" + " under l2vpn" % (router, vrf) + ) + return errormsg + + l2VpnEvpn_data = show_bgp_json[vrf]["l2VpnEvpn"][ + "peers" + ] + nh_state = l2VpnEvpn_data[neighbor_ip]["state"] + + if nh_state == "Established": + no_of_evpn_peer += 1 + + if no_of_evpn_peer == total_evpn_peer: + logger.info( + "[DUT: %s] VRF: %s, BGP is Converged for " "epvn peers", + router, + vrf, + ) + else: + errormsg = ( + "[DUT: %s] VRF: %s, BGP is not converged " + "for evpn peers" % (router, vrf) + ) + return errormsg + else: + for addr_type in bgp_addr_type.keys(): + if not check_address_types(addr_type): + continue + total_peer = 0 + + bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] + + for bgp_neighbor in bgp_neighbors: + total_peer += len(bgp_neighbors[bgp_neighbor]["dest_link"]) + + for addr_type in bgp_addr_type.keys(): + if not check_address_types(addr_type): + continue + bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] + + no_of_peer = 0 + for bgp_neighbor, peer_data in bgp_neighbors.items(): + for dest_link in peer_data["dest_link"].keys(): + data = topo["routers"][bgp_neighbor]["links"] + if dest_link in data: + peer_details = peer_data["dest_link"][dest_link] + # for link local neighbors + if ( + "neighbor_type" in peer_details + and peer_details["neighbor_type"] == "link-local" + ): + neighbor_ip = get_ipv6_linklocal_address( + topo["routers"], bgp_neighbor, dest_link + ) + elif "source_link" in peer_details: + neighbor_ip = topo["routers"][bgp_neighbor][ + "links" + ][peer_details["source_link"]][addr_type].split( + "/" + )[ + 0 + ] + elif ( + "neighbor_type" in peer_details + and peer_details["neighbor_type"] == "unnumbered" + ): + neighbor_ip = data[dest_link]["peer-interface"] + else: + neighbor_ip = data[dest_link][addr_type].split("/")[ + 0 + ] + nh_state = None + + if addr_type == "ipv4": + ipv4_data = show_bgp_json[vrf]["ipv4Unicast"][ + "peers" + ] + nh_state = ipv4_data[neighbor_ip]["state"] + else: + ipv6_data = show_bgp_json[vrf]["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("[DUT: %s] VRF: %s, BGP is Converged", router, vrf) + else: + errormsg = "[DUT: %s] VRF: %s, BGP is not converged" % (router, vrf) + return errormsg logger.debug("Exiting API: verify_bgp_convergence()") return True +@retry(attempts=3, wait=4, return_is_str=True) +def verify_bgp_community( + tgen, addr_type, router, network, input_dict=None, vrf=None, bestpath=False +): + """ + API to veiryf BGP large community is attached in route for any given + DUT by running "show bgp ipv4/6 {route address} json" command. + + Parameters + ---------- + * `tgen`: topogen object + * `addr_type` : ip type, ipv4/ipv6 + * `dut`: Device Under Test + * `network`: network for which set criteria needs to be verified + * `input_dict`: having details like - for which router, community and + values needs to be verified + * `vrf`: VRF name + * `bestpath`: To check best path cli + + Usage + ----- + networks = ["200.50.2.0/32"] + input_dict = { + "largeCommunity": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5" + } + result = verify_bgp_community(tgen, "ipv4", dut, network, input_dict=None) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: verify_bgp_community()") + if router not in tgen.routers(): + return False + + rnode = tgen.routers()[router] + + logger.info( + "Verifying BGP community attributes on dut %s: for %s " "network %s", + router, + addr_type, + network, + ) + + command = "show bgp" + + sleep(5) + for net in network: + if vrf: + cmd = "{} vrf {} {} {} json".format(command, vrf, addr_type, net) + elif bestpath: + cmd = "{} {} {} bestpath json".format(command, addr_type, net) + else: + cmd = "{} {} {} json".format(command, addr_type, net) + + show_bgp_json = run_frr_cmd(rnode, cmd, isjson=True) + if "paths" not in show_bgp_json: + return "Prefix {} not found in BGP table of router: {}".format(net, router) + + as_paths = show_bgp_json["paths"] + found = False + for i in range(len(as_paths)): + if ( + "largeCommunity" in show_bgp_json["paths"][i] + or "community" in show_bgp_json["paths"][i] + ): + found = True + logger.info( + "Large Community attribute is found for route:" " %s in router: %s", + net, + router, + ) + if input_dict is not None: + for criteria, comm_val in input_dict.items(): + show_val = show_bgp_json["paths"][i][criteria]["string"] + if comm_val == show_val: + logger.info( + "Verifying BGP %s for prefix: %s" + " in router: %s, found expected" + " value: %s", + criteria, + net, + router, + comm_val, + ) + else: + errormsg = ( + "Failed: Verifying BGP attribute" + " {} for route: {} in router: {}" + ", expected value: {} but found" + ": {}".format(criteria, net, router, comm_val, show_val) + ) + return errormsg + + if not found: + errormsg = ( + "Large Community attribute is not found for route: " + "{} in router: {} ".format(net, router) + ) + return errormsg + + logger.debug("Exiting lib API: verify_bgp_community()") + return True + + def modify_as_number(tgen, topo, input_dict): """ API reads local_as and remote_as from user defined input_dict and @@ -1090,6 +1330,7 @@ def clear_bgp(tgen, addr_type, router, vrf=None): """ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + if router not in tgen.routers(): return False @@ -1102,9 +1343,21 @@ def clear_bgp(tgen, addr_type, router, vrf=None): # Clearing BGP logger.info("Clearing BGP neighborship for router %s..", router) if addr_type == "ipv4": - run_frr_cmd(rnode, "clear ip bgp *") + if vrf: + for _vrf in vrf: + run_frr_cmd(rnode, "clear ip bgp vrf {} *".format(_vrf)) + else: + run_frr_cmd(rnode, "clear ip bgp *") elif addr_type == "ipv6": - run_frr_cmd(rnode, "clear bgp ipv6 *") + if vrf: + for _vrf in vrf: + run_frr_cmd(rnode, "clear bgp vrf {} ipv6 *".format(_vrf)) + else: + run_frr_cmd(rnode, "clear bgp ipv6 *") + else: + run_frr_cmd(rnode, "clear bgp *") + + sleep(5) logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) @@ -1698,7 +1951,6 @@ def verify_best_path_as_per_bgp_attribute( "show bgp ipv4/6 json" command will be run and verify best path according to shortest as-path, highest local-preference and med, lowest weight and route origin IGP>EGP>INCOMPLETE. - Parameters ---------- * `tgen` : topogen object @@ -1707,7 +1959,6 @@ def verify_best_path_as_per_bgp_attribute( * `attribute` : calculate best path using this attribute * `input_dict`: defines different routes to calculate for which route best path is selected - Usage ----- # To verify best path for routes 200.50.2.0/32 and 200.60.2.0/32 from @@ -1741,114 +1992,155 @@ def verify_best_path_as_per_bgp_attribute( """ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + if router not in tgen.routers(): return False rnode = tgen.routers()[router] - command = "show bgp {} json".format(addr_type) + # Verifying show bgp json + command = "show bgp" - sleep(5) + sleep(2) logger.info("Verifying router %s RIB for best path:", router) - sh_ip_bgp_json = run_frr_cmd(rnode, command, isjson=True) + static_route = False + advertise_network = False for route_val in input_dict.values(): - net_data = route_val["bgp"]["address_family"][addr_type]["unicast"] - networks = net_data["advertise_networks"] + if "static_routes" in route_val: + static_route = True + networks = route_val["static_routes"] + else: + advertise_network = True + net_data = route_val["bgp"]["address_family"][addr_type]["unicast"] + networks = net_data["advertise_networks"] + for network in networks: - route = network["network"] + _network = network["network"] + no_of_ip = network.setdefault("no_of_ip", 1) + vrf = network.setdefault("vrf", None) - route_attributes = sh_ip_bgp_json["routes"][route] - _next_hop = None - compare = None - attribute_dict = {} - for route_attribute in route_attributes: - next_hops = route_attribute["nexthops"] - for next_hop in next_hops: - next_hop_ip = next_hop["ip"] - attribute_dict[next_hop_ip] = route_attribute[attribute] - - # AS_PATH attribute - if attribute == "path": - # Find next_hop for the route have minimum as_path - _next_hop = min( - attribute_dict, key=lambda x: len(set(attribute_dict[x])) - ) - compare = "SHORTEST" - - # LOCAL_PREF attribute - elif attribute == "locPrf": - # Find next_hop for the route have highest local preference - _next_hop = max(attribute_dict, key=(lambda k: attribute_dict[k])) - compare = "HIGHEST" - - # WEIGHT attribute - elif attribute == "weight": - # Find next_hop for the route have highest weight - _next_hop = max(attribute_dict, key=(lambda k: attribute_dict[k])) - compare = "HIGHEST" - - # ORIGIN attribute - elif attribute == "origin": - # Find next_hop for the route have IGP as origin, - - # - rule is IGP>EGP>INCOMPLETE - _next_hop = [ - key for (key, value) in attribute_dict.iteritems() if value == "IGP" - ][0] - compare = "" - - # MED attribute - elif attribute == "metric": - # Find next_hop for the route have LOWEST MED - _next_hop = min(attribute_dict, key=(lambda k: attribute_dict[k])) - compare = "LOWEST" - - # Show ip route - if addr_type == "ipv4": - command = "show ip route json" + if vrf: + cmd = "{} vrf {}".format(command, vrf) else: - command = "show ipv6 route json" + cmd = command - rib_routes_json = run_frr_cmd(rnode, command, isjson=True) + cmd = "{} {}".format(cmd, addr_type) + cmd = "{} json".format(cmd) + sh_ip_bgp_json = run_frr_cmd(rnode, cmd, isjson=True) - # Verifying output dictionary rib_routes_json is not empty - if not bool(rib_routes_json): - errormsg = "No route found in RIB of router {}..".format(router) - return errormsg + routes = generate_ips(_network, no_of_ip) + for route in routes: + route = str(ipaddr.IPNetwork(unicode(route))) - st_found = False - nh_found = False - # Find best is installed in RIB - if route in rib_routes_json: - st_found = True - # Verify next_hop in rib_routes_json - if rib_routes_json[route][0]["nexthops"][0]["ip"] in attribute_dict: - nh_found = True - else: - errormsg = ( - "Incorrect Nexthop for BGP route {} in " - "RIB of router {}, Expected: {}, Found:" - " {}\n".format( - route, - router, - rib_routes_json[route][0]["nexthops"][0]["ip"], - _next_hop, + if route in sh_ip_bgp_json["routes"]: + route_attributes = sh_ip_bgp_json["routes"][route] + _next_hop = None + compare = None + attribute_dict = {} + for route_attribute in route_attributes: + next_hops = route_attribute["nexthops"] + for next_hop in next_hops: + next_hop_ip = next_hop["ip"] + attribute_dict[next_hop_ip] = route_attribute[attribute] + + # AS_PATH attribute + if attribute == "path": + # Find next_hop for the route have minimum as_path + _next_hop = min( + attribute_dict, key=lambda x: len(set(attribute_dict[x])) ) - ) - return errormsg + compare = "SHORTEST" - if st_found and nh_found: - logger.info( - "Best path for prefix: %s with next_hop: %s is " - "installed according to %s %s: (%s) in RIB of " - "router %s", - route, - _next_hop, - compare, - attribute, - attribute_dict[_next_hop], - router, - ) + # LOCAL_PREF attribute + elif attribute == "locPrf": + # Find next_hop for the route have highest local preference + _next_hop = max( + attribute_dict, key=(lambda k: attribute_dict[k]) + ) + compare = "HIGHEST" + + # WEIGHT attribute + elif attribute == "weight": + # Find next_hop for the route have highest weight + _next_hop = max( + attribute_dict, key=(lambda k: attribute_dict[k]) + ) + compare = "HIGHEST" + + # ORIGIN attribute + elif attribute == "origin": + # Find next_hop for the route have IGP as origin, - + # - rule is IGP>EGP>INCOMPLETE + _next_hop = [ + key + for (key, value) in attribute_dict.iteritems() + if value == "IGP" + ][0] + compare = "" + + # MED attribute + elif attribute == "metric": + # Find next_hop for the route have LOWEST MED + _next_hop = min( + attribute_dict, key=(lambda k: attribute_dict[k]) + ) + compare = "LOWEST" + + # Show ip route + if addr_type == "ipv4": + command_1 = "show ip route" + else: + command_1 = "show ipv6 route" + + if vrf: + cmd = "{} vrf {} json".format(command_1, vrf) + else: + cmd = "{} json".format(command_1) + + rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True) + + # Verifying output dictionary rib_routes_json is not empty + if not bool(rib_routes_json): + errormsg = "No route found in RIB of router {}..".format(router) + return errormsg + + st_found = False + nh_found = False + # Find best is installed in RIB + if route in rib_routes_json: + st_found = True + # Verify next_hop in rib_routes_json + if ( + rib_routes_json[route][0]["nexthops"][0]["ip"] + in attribute_dict + ): + nh_found = True + else: + errormsg = ( + "Incorrect Nexthop for BGP route {} in " + "RIB of router {}, Expected: {}, Found:" + " {}\n".format( + route, + router, + rib_routes_json[route][0]["nexthops"][0]["ip"], + _next_hop, + ) + ) + return errormsg + + if st_found and nh_found: + logger.info( + "Best path for prefix: %s with next_hop: %s is " + "installed according to %s %s: (%s) in RIB of " + "router %s", + route, + _next_hop, + compare, + attribute, + attribute_dict[_next_hop], + router, + ) logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return True @@ -1965,7 +2257,7 @@ def verify_best_path_as_per_admin_distance( return True -@retry(attempts=10, wait=2, return_is_str=True, initial_wait=2) +@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2) def verify_bgp_rib(tgen, addr_type, dut, input_dict, next_hop=None, aspath=None): """ This API is to verify whether bgp rib has any @@ -1995,7 +2287,7 @@ def verify_bgp_rib(tgen, addr_type, dut, input_dict, next_hop=None, aspath=None) errormsg(str) or True """ - logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + logger.debug("Entering lib API: verify_bgp_rib()") router_list = tgen.routers() additional_nexthops_in_required_nhs = [] @@ -2210,7 +2502,7 @@ def verify_bgp_rib(tgen, addr_type, dut, input_dict, next_hop=None, aspath=None) "routes are: {}\n".format(dut, found_routes) ) - logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + logger.debug("Exiting lib API: verify_bgp_rib()") return True diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index 0b19877aff..3de7ab3ebe 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -175,6 +175,49 @@ def run_frr_cmd(rnode, cmd, isjson=False): raise InvalidCLIError("No actual cmd passed") +def apply_raw_config(tgen, input_dict): + + """ + API to configure raw configuration on device. This can be used for any cli + which does has not been implemented in JSON. + + Parameters + ---------- + * `tgen`: tgen onject + * `input_dict`: configuration that needs to be applied + + Usage + ----- + input_dict = { + "r2": { + "raw_config": [ + "router bgp", + "no bgp update-group-split-horizon" + ] + } + } + Returns + ------- + True or errormsg + """ + + result = True + for router_name in input_dict.keys(): + config_cmd = input_dict[router_name]["raw_config"] + + if not isinstance(config_cmd, list): + config_cmd = [config_cmd] + + frr_cfg_file = "{}/{}/{}".format(TMPDIR, router_name, FRRCFG_FILE) + with open(frr_cfg_file, "w") as cfg: + for cmd in config_cmd: + cfg.write("{}\n".format(cmd)) + + result = load_config_to_router(tgen, router_name) + + return result + + def create_common_configuration( tgen, router, data, config_type=None, build=False, load_config=True ): @@ -207,6 +250,7 @@ def create_common_configuration( "bgp_community_list": "! Community List Config\n", "route_maps": "! Route Maps Config\n", "bgp": "! BGP Config\n", + "vrf": "! VRF Config\n", } ) @@ -355,6 +399,7 @@ def reset_config_on_routers(tgen, routerName=None): """ Resets configuration on routers to the snapshot created using input JSON file. It replaces existing router configuration with FRRCFG_BKUP_FILE + Parameters ---------- * `tgen` : Topogen object @@ -370,6 +415,7 @@ def reset_config_on_routers(tgen, routerName=None): 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) @@ -387,22 +433,13 @@ def reset_config_on_routers(tgen, routerName=None): f.write("\n") f.close() - run_cfg_file = "{}/{}/frr.sav".format(TMPDIR, rname) init_cfg_file = "{}/{}/frr_json_initial.conf".format(TMPDIR, rname) - - tempdir = mkdtemp() - with open(os.path.join(tempdir, "vtysh.conf"), "w") as fd: - pass - - command = "/usr/lib/frr/frr-reload.py --confdir {} --input {} --test {} > {}".format( - tempdir, run_cfg_file, init_cfg_file, dname + 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) - os.unlink(os.path.join(tempdir, "vtysh.conf")) - os.rmdir(tempdir) - # Assert if command fail if result > 0: logger.error("Delta file creation failed. Command executed %s", command) @@ -459,17 +496,18 @@ 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("Configuration on router {} after reset:".format(rname)) logger.info(delta.getvalue()) delta.close() - logger.debug("Exting API: reset_config_on_routers") + logger.debug("Exiting API: reset_config_on_routers") return True def load_config_to_router(tgen, routerName, save_bkup=False): """ Loads configuration on router from the file FRRCFG_FILE. + Parameters ---------- * `tgen` : Topogen object @@ -481,7 +519,7 @@ def load_config_to_router(tgen, routerName, save_bkup=False): router_list = tgen.routers() for rname in ROUTER_LIST: - if routerName and routerName != rname: + if routerName and rname != routerName: continue router = router_list[rname] @@ -504,6 +542,7 @@ def load_config_to_router(tgen, routerName, save_bkup=False): raise InvalidCLIError("%s" % output) cfg.truncate(0) + except IOError as err: errormsg = ( "Unable to open config File. error(%s):" " %s", @@ -514,24 +553,29 @@ def load_config_to_router(tgen, routerName, save_bkup=False): # Router current configuration to log file or console if # "show_router_config" is defined in "pytest.ini" if show_router_config: + logger.info("New configuration for router {}:".format(rname)) new_config = router.run("vtysh -c 'show running'") logger.info(new_config) - logger.debug("Exting API: load_config_to_router") + logger.debug("Exiting API: load_config_to_router") return True -def get_frr_ipv6_linklocal(tgen, router, intf=None): +def get_frr_ipv6_linklocal(tgen, router, intf=None, vrf=None): """ API to get the link local ipv6 address of a perticular interface using FRR command 'show interface' + * `tgen`: tgen onject * `router` : router for which hightest interface should be calculated * `intf` : interface for which linklocal address needs to be taken + * `vrf` : VRF name + Usage ----- linklocal = get_frr_ipv6_linklocal(tgen, router, "intf1", RED_A) + Returns ------- 1) array of interface names to link local ips. @@ -544,7 +588,10 @@ def get_frr_ipv6_linklocal(tgen, router, intf=None): linklocal = [] - cmd = "show interface" + if vrf: + cmd = "show interface vrf {}".format(vrf) + else: + cmd = "show interface" ifaces = router_list[router].run('vtysh -c "{}"'.format(cmd)) @@ -635,6 +682,55 @@ def start_topology(tgen): tgen.start_router() +def stop_router(tgen, router): + """ + Router"s current config would be saved to /etc/frr/ for each deamon + and router and its deamons would be stopped. + + * `tgen` : topogen object + * `router`: Device under test + """ + + router_list = tgen.routers() + + # Saving router config to /etc/frr, which will be loaded to router + # when it starts + router_list[router].vtysh_cmd("write memory") + + # Stop router + router_list[router].stop() + + +def start_router(tgen, router): + """ + Router will started and config would be loaded from /etc/frr/ for each + deamon + + * `tgen` : topogen object + * `router`: Device under test + """ + + logger.debug("Entering lib API: start_router") + + try: + router_list = tgen.routers() + + # Router and its deamons would be started and config would + # be loaded to router for each deamon from /etc/frr + router_list[router].start() + + # Waiting for router to come up + sleep(5) + + except Exception as e: + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: start_router()") + return True + + def number_to_row(routerName): """ Returns the number for the router. @@ -658,6 +754,190 @@ def number_to_column(routerName): ############################################# +def create_vrf_cfg(tgen, topo, input_dict=None, build=False): + """ + Create vrf configuration for created topology. VRF + configuration is provided in input json file. + + VRF config is done in Linux Kernel: + * Create VRF + * Attach interface to VRF + * Bring up VRF + + Parameters + ---------- + * `tgen` : Topogen object + * `topo` : json file data + * `input_dict` : Input dict data, required when configuring + from testcase + * `build` : Only for initial setup phase this is set as True. + + Usage + ----- + input_dict={ + "r3": { + "links": { + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"}, + "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}, + "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"}, + "r2-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}, + }, + "vrfs":[ + { + "name": "RED_A", + "id": "1" + }, + { + "name": "RED_B", + "id": "2" + }, + { + "name": "BLUE_A", + "id": "3", + "delete": True + }, + { + "name": "BLUE_B", + "id": "4" + } + ] + } + } + result = create_vrf_cfg(tgen, topo, input_dict) + + Returns + ------- + True or False + """ + result = True + if not input_dict: + input_dict = deepcopy(topo) + else: + input_dict = deepcopy(input_dict) + + try: + for c_router, c_data in input_dict.iteritems(): + rnode = tgen.routers()[c_router] + if "vrfs" in c_data: + for vrf in c_data["vrfs"]: + config_data = [] + del_action = vrf.setdefault("delete", False) + name = vrf.setdefault("name", None) + table_id = vrf.setdefault("id", None) + vni = vrf.setdefault("vni", None) + del_vni = vrf.setdefault("no_vni", None) + + if del_action: + # Kernel cmd- Add VRF and table + cmd = "ip link del {} type vrf table {}".format( + vrf["name"], vrf["id"] + ) + + logger.info("[DUT: %s]: Running kernel cmd [%s]", c_router, cmd) + rnode.run(cmd) + + # Kernel cmd - Bring down VRF + cmd = "ip link set dev {} down".format(name) + logger.info("[DUT: %s]: Running kernel cmd [%s]", c_router, cmd) + rnode.run(cmd) + + else: + if name and table_id: + # Kernel cmd- Add VRF and table + cmd = "ip link add {} type vrf table {}".format( + name, table_id + ) + logger.info( + "[DUT: %s]: Running kernel cmd " "[%s]", c_router, cmd + ) + rnode.run(cmd) + + # Kernel cmd - Bring up VRF + cmd = "ip link set dev {} up".format(name) + logger.info( + "[DUT: %s]: Running kernel " "cmd [%s]", c_router, cmd + ) + rnode.run(cmd) + + if "links" in c_data: + for destRouterLink, data in sorted( + c_data["links"].iteritems() + ): + # Loopback interfaces + if "type" in data and data["type"] == "loopback": + interface_name = destRouterLink + else: + interface_name = data["interface"] + + if "vrf" in data: + vrf_list = data["vrf"] + + if type(vrf_list) is not list: + vrf_list = [vrf_list] + + for _vrf in vrf_list: + cmd = "ip link set {} master {}".format( + interface_name, _vrf + ) + + logger.info( + "[DUT: %s]: Running" " kernel cmd [%s]", + c_router, + cmd, + ) + rnode.run(cmd) + + result = create_common_configuration( + tgen, c_router, config_data, "vrf", build=build + ) + + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + return result + + +def create_interface_in_kernel( + tgen, dut, name, ip_addr, vrf=None, netmask=None, create=True +): + """ + Cretae interfaces in kernel for ipv4/ipv6 + Config is done in Linux Kernel: + + Parameters + ---------- + * `tgen` : Topogen object + * `dut` : Device for which interfaces to be added + * `name` : interface name + * `ip_addr` : ip address for interface + * `vrf` : VRF name, to which interface will be associated + * `netmask` : netmask value, default is None + * `create`: Create interface in kernel, if created then no need + to create + """ + + rnode = tgen.routers()[dut] + + if create: + cmd = "sudo ip link add name {} type dummy".format(name) + rnode.run(cmd) + + addr_type = validate_ip_address(ip_addr) + if addr_type == "ipv4": + cmd = "ifconfig {} {} netmask {}".format(name, ip_addr, netmask) + else: + cmd = "ifconfig {} inet6 add {}/{}".format(name, ip_addr, netmask) + + rnode.run(cmd) + + if vrf: + cmd = "ip link set {} master {}".format(name, vrf) + rnode.run(cmd) + + def validate_ip_address(ip_address): """ Validates the type of ip address @@ -860,13 +1140,15 @@ def interface_status(tgen, topo, input_dict): return True -def retry(attempts=3, wait=2, return_is_str=True, initial_wait=0): +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 + * `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): @@ -883,15 +1165,20 @@ def retry(attempts=3, wait=2, return_is_str=True, initial_wait=0): 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) 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) and _expected: + if _return_is_str and isinstance(ret, bool) and _expected: return ret - if isinstance(ret, str) 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: @@ -945,16 +1232,19 @@ def create_interfaces_cfg(tgen, topo, build=False): """ Create interface configuration for created topology. Basic Interface configuration is provided in input json file. + Parameters ---------- * `tgen` : Topogen object * `topo` : json file data * `build` : Only for initial setup phase this is set as True. + Returns ------- True or False """ result = False + topo = deepcopy(topo) try: for c_router, c_data in topo.iteritems(): @@ -965,13 +1255,30 @@ def create_interfaces_cfg(tgen, topo, build=False): interface_name = destRouterLink else: interface_name = data["interface"] + interface_data.append("interface {}".format(str(interface_name))) if "ipv4" in data: intf_addr = c_data["links"][destRouterLink]["ipv4"] - interface_data.append("ip address {}".format(intf_addr)) + + if "delete" in data and data["delete"]: + interface_data.append("no ip address {}".format(intf_addr)) + else: + interface_data.append("ip address {}".format(intf_addr)) if "ipv6" in data: intf_addr = c_data["links"][destRouterLink]["ipv6"] - interface_data.append("ipv6 address {}".format(intf_addr)) + + if "delete" in data and data["delete"]: + interface_data.append("no ipv6 address {}".format(intf_addr)) + else: + interface_data.append("ipv6 address {}".format(intf_addr)) + + if "ipv6-link-local" in data: + intf_addr = c_data["links"][destRouterLink]["ipv6-link-local"] + + if "delete" in data and data["delete"]: + interface_data.append("no ipv6 address {}".format(intf_addr)) + else: + interface_data.append("ipv6 address {}\n".format(intf_addr)) result = create_common_configuration( tgen, c_router, interface_data, "interface_config", build=build @@ -988,11 +1295,13 @@ def create_interfaces_cfg(tgen, topo, build=False): def create_static_routes(tgen, input_dict, build=False): """ Create static routes for given router as defined in input_dict + Parameters ---------- * `tgen` : Topogen object * `input_dict` : Input dict data, required when configuring from testcase * `build` : Only for initial setup phase this is set as True. + Usage ----- input_dict should be in the format below: @@ -1002,7 +1311,9 @@ def create_static_routes(tgen, input_dict, build=False): # admin_distance: admin distance for route/routes. # next_hop: starting next-hop address # tag: tag id for static routes + # vrf: VRF name in which static routes needs to be created # delete: True if config to be removed. Default False. + Example: "routers": { "r1": { @@ -1012,24 +1323,27 @@ def create_static_routes(tgen, input_dict, build=False): "no_of_ip": 9, "admin_distance": 100, "next_hop": "10.0.0.1", - "tag": 4001 + "tag": 4001, + "vrf": "RED_A" "delete": true } ] } } + Returns ------- errormsg(str) or True """ result = False - logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + logger.debug("Entering lib API: create_static_routes()") input_dict = deepcopy(input_dict) + try: for router in input_dict.keys(): if "static_routes" not in input_dict[router]: errormsg = "static_routes not present in input_dict" - logger.debug(errormsg) + logger.info(errormsg) continue static_routes_list = [] @@ -1037,27 +1351,38 @@ def create_static_routes(tgen, input_dict, build=False): static_routes = input_dict[router]["static_routes"] for static_route in static_routes: del_action = static_route.setdefault("delete", False) - # No of IPs no_of_ip = static_route.setdefault("no_of_ip", 1) - admin_distance = static_route.setdefault("admin_distance", None) - tag = static_route.setdefault("tag", None) - if "next_hop" not in static_route or "network" not in static_route: - errormsg = "'next_hop' or 'network' missing in" " input_dict" - return errormsg - - next_hop = static_route["next_hop"] - network = static_route["network"] + network = static_route.setdefault("network", []) if type(network) is not list: network = [network] + admin_distance = static_route.setdefault("admin_distance", None) + tag = static_route.setdefault("tag", None) + vrf = static_route.setdefault("vrf", None) + interface = static_route.setdefault("interface", None) + next_hop = static_route.setdefault("next_hop", None) + nexthop_vrf = static_route.setdefault("nexthop_vrf", None) + ip_list = generate_ips(network, no_of_ip) for ip in ip_list: addr_type = validate_ip_address(ip) if addr_type == "ipv4": - cmd = "ip route {} {}".format(ip, next_hop) + cmd = "ip route {}".format(ip) else: - cmd = "ipv6 route {} {}".format(ip, next_hop) + cmd = "ipv6 route {}".format(ip) + + if interface: + cmd = "{} {}".format(cmd, interface) + + if next_hop: + cmd = "{} {}".format(cmd, next_hop) + + if nexthop_vrf: + cmd = "{} nexthop-vrf {}".format(cmd, nexthop_vrf) + + if vrf: + cmd = "{} vrf {}".format(cmd, vrf) if tag: cmd = "{} tag {}".format(cmd, str(tag)) @@ -1080,7 +1405,7 @@ def create_static_routes(tgen, input_dict, build=False): logger.error(errormsg) return errormsg - logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + logger.debug("Exiting lib API: create_static_routes()") return result @@ -1433,6 +1758,9 @@ def create_route_maps(tgen, input_dict, build=False): "large_community_list", {} ) + metric = match_data.setdefault("metric", None) + source_vrf = match_data.setdefault("source-vrf", None) + if ipv4_data: # fetch prefix list data from rmap prefix_name = ipv4_data.setdefault("prefix_lists", None) @@ -1525,6 +1853,14 @@ def create_route_maps(tgen, input_dict, build=False): cmd = "{} exact-match".format(cmd) rmap_data.append(cmd) + if source_vrf: + cmd = "match source-vrf {}".format(source_vrf) + rmap_data.append(cmd) + + if metric: + cmd = "match metric {}".format(metric) + rmap_data.append(cmd) + result = create_common_configuration( tgen, router, rmap_data, "route_maps", build=build ) @@ -1689,17 +2025,97 @@ def shutdown_bringup_interface(tgen, dut, intf_name, ifaceaction=False): interface_set_status(router_list[dut], intf_name, ifaceaction) +def addKernelRoute( + tgen, router, intf, group_addr_range, next_hop=None, src=None, del_action=None +): + """ + Add route to kernel + + Parameters: + ----------- + * `tgen` : Topogen object + * `router`: router for which kernal routes needs to be added + * `intf`: interface name, for which kernal routes needs to be added + * `bindToAddress`: bind to , an interface or multicast + address + + returns: + -------- + errormsg or True + """ + + logger.debug("Entering lib API: addKernelRoute()") + + rnode = tgen.routers()[router] + + if type(group_addr_range) is not list: + group_addr_range = [group_addr_range] + + for grp_addr in group_addr_range: + + addr_type = validate_ip_address(grp_addr) + if addr_type == "ipv4": + if next_hop is not None: + cmd = "ip route add {} via {}".format(grp_addr, next_hop) + else: + cmd = "ip route add {} dev {}".format(grp_addr, intf) + if del_action: + cmd = "ip route del {}".format(grp_addr) + verify_cmd = "ip route" + elif addr_type == "ipv6": + if intf and src: + cmd = "ip -6 route add {} dev {} src {}".format(grp_addr, intf, src) + else: + cmd = "ip -6 route add {} via {}".format(grp_addr, next_hop) + verify_cmd = "ip -6 route" + if del_action: + cmd = "ip -6 route del {}".format(grp_addr) + + logger.info("[DUT: {}]: Running command: [{}]".format(router, cmd)) + output = rnode.run(cmd) + + # Verifying if ip route added to kernal + result = rnode.run(verify_cmd) + logger.debug("{}\n{}".format(verify_cmd, result)) + if "/" in grp_addr: + ip, mask = grp_addr.split("/") + if mask == "32" or mask == "128": + grp_addr = ip + + if not re_search(r"{}".format(grp_addr), result) and mask is not "0": + errormsg = ( + "[DUT: {}]: Kernal route is not added for group" + " address {} Config output: {}".format(router, grp_addr, output) + ) + + return errormsg + + logger.debug("Exiting lib API: addKernelRoute()") + return True + + ############################################# # Verification APIs ############################################# -@retry(attempts=10, return_is_str=True, initial_wait=2) -def verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None): +@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2) +def verify_rib( + tgen, + addr_type, + dut, + input_dict, + next_hop=None, + protocol=None, + tag=None, + metric=None, + fib=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 do will verify next_hop and each prefix/routes is present in "show ip/ipv6 route {bgp/stataic} json" command o/p. + Parameters ---------- * `tgen` : topogen object @@ -1709,12 +2125,14 @@ def verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None): * `next_hop`[optional]: next_hop which needs to be verified, default: static * `protocol`[optional]: protocol, default = None + Usage ----- # RIB can be verified for static routes OR network advertised using network command. Following are input_dicts to create static routes and advertise networks using network command. Any one of the input_dict can be passed to verify_rib() to verify routes in DUT"s RIB. + # Creating static routes for r1 input_dict = { "r1": { @@ -1732,186 +2150,328 @@ def verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None): dut = "r2" protocol = "bgp" result = verify_rib(tgen, "ipv4", dut, input_dict, protocol = protocol) + Returns ------- errormsg(str) or True """ - logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + logger.info("Entering lib API: verify_rib()") router_list = tgen.routers() + additional_nexthops_in_required_nhs = [] + found_hops = [] for routerInput in input_dict.keys(): for router, rnode in router_list.iteritems(): if router != dut: continue + logger.info("Checking router %s RIB:", router) + # Verifying RIB routes if addr_type == "ipv4": - if protocol: - command = "show ip route {} json".format(protocol) - else: - command = "show ip route json" + command = "show ip route" else: - if protocol: - command = "show ipv6 route {} json".format(protocol) - else: - command = "show ipv6 route json" + command = "show ipv6 route" - logger.info("Checking router %s RIB:", router) - 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: - errormsg = "No {} route found in rib of router {}..".format( - protocol, router - ) - return errormsg + found_routes = [] + missing_routes = [] if "static_routes" in input_dict[routerInput]: static_routes = input_dict[routerInput]["static_routes"] - st_found = False - nh_found = False - found_routes = [] - missing_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) + + if protocol: + cmd = "{} {}".format(cmd, protocol) + + 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 = "No route found in rib of router {}..".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 + if "tag" in static_route: + _tag = static_route["tag"] + else: + _tag = None + # 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(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 fib and next_hop: if type(next_hop) is not list: next_hop = [next_hop] + for mnh in range(0, len(rib_routes_json[st_rt])): + if ( + "fib" + in rib_routes_json[st_rt][mnh]["nexthops"][0] + ): + found_hops.append( + [ + rib_r["ip"] + for rib_r in rib_routes_json[st_rt][ + mnh + ]["nexthops"] + ] + ) + + if found_hops[0]: + missing_list_of_nexthops = set( + found_hops[0] + ).difference(next_hop) + additional_nexthops_in_required_nhs = set( + next_hop + ).difference(found_hops[0]) + + if additional_nexthops_in_required_nhs: + logger.info( + "Nexthop " + "%s is not active for route %s in " + "RIB of router %s\n", + additional_nexthops_in_required_nhs, + st_rt, + dut, + ) + errormsg = ( + "Nexthop {} is not active" + " for route {} in RIB of router" + " {}\n".format( + additional_nexthops_in_required_nhs, + st_rt, + dut, + ) + ) + return errormsg + else: + nh_found = True + + elif next_hop and fib is None: + if type(next_hop) is not list: + next_hop = [next_hop] found_hops = [ rib_r["ip"] for rib_r in rib_routes_json[st_rt][0]["nexthops"] ] - for nh in found_hops: - nh_found = False - if nh and nh in next_hop: - nh_found = True - else: + + if found_hops: + missing_list_of_nexthops = set( + found_hops + ).difference(next_hop) + additional_nexthops_in_required_nhs = set( + next_hop + ).difference(found_hops) + + if additional_nexthops_in_required_nhs: + logger.info( + "Missing nexthop %s for route" + " %s in RIB of router %s\n", + additional_nexthops_in_required_nhs, + st_rt, + dut, + ) errormsg = ( - "Nexthop {} is Missing for {}" - " route {} in RIB of router" - " {}\n".format( - next_hop, protocol, st_rt, dut + "Nexthop {} is Missing for " + "route {} in RIB of router {}\n".format( + additional_nexthops_in_required_nhs, + st_rt, + dut, ) ) - return errormsg + else: + nh_found = True + + if tag: + if "tag" not in rib_routes_json[st_rt][0]: + errormsg = ( + "[DUT: {}]: tag is not" + " present for" + " route {} in RIB \n".format(dut, st_rt) + ) + return errormsg + + if _tag != rib_routes_json[st_rt][0]["tag"]: + errormsg = ( + "[DUT: {}]: tag value {}" + " is not matched for" + " route {} in RIB \n".format(dut, _tag, st_rt,) + ) + return errormsg + + if metric is not None: + if "metric" not in rib_routes_json[st_rt][0]: + errormsg = ( + "[DUT: {}]: metric is" + " not present for" + " route {} in RIB \n".format(dut, st_rt) + ) + return errormsg + + if metric != rib_routes_json[st_rt][0]["metric"]: + errormsg = ( + "[DUT: {}]: metric value " + "{} is not matched for " + "route {} in RIB \n".format(dut, metric, st_rt,) + ) + return errormsg + else: missing_routes.append(st_rt) if nh_found: logger.info( - "Found next_hop %s for all routes in RIB of" " router %s\n", - next_hop, - dut, + "[DUT: {}]: Found next_hop {} for all bgp" + " routes in RIB".format(router, next_hop) ) - if not st_found and len(missing_routes) > 0: - errormsg = ( - "Missing route in RIB of router {}, routes: " - "{}\n".format(dut, missing_routes) + if len(missing_routes) > 0: + errormsg = "[DUT: {}]: Missing route in RIB, " "routes: {}".format( + dut, missing_routes ) return errormsg - logger.info( - "Verified routes in router %s RIB, found routes" " are: %s\n", - dut, - found_routes, - ) + if found_routes: + logger.info( + "[DUT: %s]: Verified routes in RIB, found" " routes are: %s\n", + dut, + found_routes, + ) continue if "bgp" in input_dict[routerInput]: if ( "advertise_networks" - in input_dict[routerInput]["bgp"]["address_family"][addr_type][ + 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"] + found_routes = [] + missing_routes = [] + advertise_network = input_dict[routerInput]["bgp"]["address_family"][ + addr_type + ]["unicast"]["advertise_networks"] - for advertise_network_dict in advertise_network: - 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 + # Continue if there are no network advertise + if len(advertise_network) == 0: + continue - # Generating IPs for verification - ip_list = generate_ips(start_ip, no_of_network) - for st_rt in ip_list: - st_rt = str(ipaddr.IPNetwork(unicode(st_rt))) + 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) - found = False - nh_found = False - if st_rt in rib_routes_json: - found = True - found_routes.append(st_rt) + rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True) - if next_hop: - if type(next_hop) is not list: - next_hop = [next_hop] + # 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 - for nh in next_hop: - for nh_json in rib_routes_json[st_rt][0][ - "nexthops" - ]: - if nh != nh_json["ip"]: - continue - nh_found = True + 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 - if not nh_found: - errormsg = ( - "Nexthop {} is Missing" - " for {} route {} in " - "RIB of router {}\n".format( - next_hop, protocol, st_rt, dut - ) - ) - return errormsg + # 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))) + + _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 nh_found: - logger.info( - "Found next_hop {} for all routes in RIB" - " of router {}\n".format(next_hop, dut) - ) + if nh_found: + logger.info( + "Found next_hop {} for all routes in RIB" + " of router {}\n".format(next_hop, dut) + ) - if not found and len(missing_routes) > 0: - errormsg = ( - "Missing {} route in RIB of router {}, " - "routes: {} \n".format(addr_type, dut, missing_routes) - ) - return errormsg + if len(missing_routes) > 0: + errormsg = ( + "Missing {} route in RIB of router {}, " + "routes: {} \n".format(addr_type, dut, missing_routes) + ) + return errormsg + if found_routes: logger.info( "Verified {} routes in router {} RIB, found" " routes are: {}\n".format(addr_type, dut, found_routes) ) - logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + logger.info("Exiting lib API: verify_rib()") return True diff --git a/tests/topotests/lib/topojson.py b/tests/topotests/lib/topojson.py index b25317ba7f..24b61981d6 100644 --- a/tests/topotests/lib/topojson.py +++ b/tests/topotests/lib/topojson.py @@ -37,6 +37,7 @@ from lib.common_config import ( create_prefix_lists, create_route_maps, create_bgp_community_lists, + create_vrf_cfg, ) from lib.bgp import create_router_bgp @@ -49,7 +50,6 @@ def build_topo_from_json(tgen, topo): Reads configuration from JSON file. Adds routers, creates interface names dynamically and link routers as defined in JSON to create topology. Assigns IPs dynamically to all interfaces of each router. - * `tgen`: Topogen object * `topo`: json file data """ @@ -203,6 +203,7 @@ def build_config_from_json(tgen, topo, save_bkup=True): func_dict = OrderedDict( [ + ("vrfs", create_vrf_cfg), ("links", create_interfaces_cfg), ("static_routes", create_static_routes), ("prefix_lists", create_prefix_lists),