]> git.proxmox.com Git - mirror_frr.git/blobdiff - tests/topotests/isis_sr_te_topo1/test_isis_sr_te_topo1.py
Merge pull request #12816 from gpnaveen/stc_rte_err_msg
[mirror_frr.git] / tests / topotests / isis_sr_te_topo1 / test_isis_sr_te_topo1.py
index 6bbb5702679bc523e4085c7c61dc13bd5fa081c8..fc2ce7cb4b666bd915d6e828f05de29644374b79 100755 (executable)
@@ -1,4 +1,5 @@
 #!/usr/bin/env python
+# SPDX-License-Identifier: ISC
 
 #
 # test_isis_sr_topo1.py
@@ -7,20 +8,6 @@
 # Copyright (c) 2019 by
 # Network Device Education Foundation, Inc. ("NetDEF")
 #
-# Permission to use, copy, modify, and/or distribute this software
-# for any purpose with or without fee is hereby granted, provided
-# that the above copyright notice and this permission notice appear
-# in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
-# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
-# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
-# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
-# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
-# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
-# OF THIS SOFTWARE.
-#
 
 """
 test_isis_sr_te_topo1.py:
@@ -79,8 +66,6 @@ import os
 import sys
 import pytest
 import json
-import re
-from time import sleep
 from functools import partial
 
 # Save the Current Working Directory to find configuration files.
@@ -94,69 +79,64 @@ from lib.topogen import Topogen, TopoRouter, get_topogen
 from lib.topolog import logger
 
 # Required to instantiate the topology builder class.
-from mininet.topo import Topo
 
 pytestmark = [pytest.mark.bgpd, pytest.mark.isisd, pytest.mark.pathd]
 
 
-class TemplateTopo(Topo):
-    "Test topology builder"
+def build_topo(tgen):
+    "Build function"
 
-    def build(self, *_args, **_opts):
-        "Build function"
-        tgen = get_topogen(self)
+    #
+    # Define FRR Routers
+    #
+    for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "dst"]:
+        tgen.add_router(router)
 
-        #
-        # Define FRR Routers
-        #
-        for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "dst"]:
-            tgen.add_router(router)
+    #
+    # Define connections
+    #
+    switch = tgen.add_switch("s1")
+    switch.add_link(tgen.gears["rt1"], nodeif="eth-sw1")
+    switch.add_link(tgen.gears["rt2"], nodeif="eth-sw1")
+    switch.add_link(tgen.gears["rt3"], nodeif="eth-sw1")
 
-        #
-        # Define connections
-        #
-        switch = tgen.add_switch("s1")
-        switch.add_link(tgen.gears["rt1"], nodeif="eth-sw1")
-        switch.add_link(tgen.gears["rt2"], nodeif="eth-sw1")
-        switch.add_link(tgen.gears["rt3"], nodeif="eth-sw1")
+    switch = tgen.add_switch("s2")
+    switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4-1")
+    switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2-1")
 
-        switch = tgen.add_switch("s2")
-        switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4-1")
-        switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2-1")
+    switch = tgen.add_switch("s3")
+    switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4-2")
+    switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2-2")
 
-        switch = tgen.add_switch("s3")
-        switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4-2")
-        switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2-2")
+    switch = tgen.add_switch("s4")
+    switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5-1")
+    switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3-1")
 
-        switch = tgen.add_switch("s4")
-        switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5-1")
-        switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3-1")
+    switch = tgen.add_switch("s5")
+    switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5-2")
+    switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3-2")
 
-        switch = tgen.add_switch("s5")
-        switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5-2")
-        switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3-2")
+    switch = tgen.add_switch("s6")
+    switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5")
+    switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4")
 
-        switch = tgen.add_switch("s6")
-        switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5")
-        switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4")
+    switch = tgen.add_switch("s7")
+    switch.add_link(tgen.gears["rt4"], nodeif="eth-rt6")
+    switch.add_link(tgen.gears["rt6"], nodeif="eth-rt4")
 
-        switch = tgen.add_switch("s7")
-        switch.add_link(tgen.gears["rt4"], nodeif="eth-rt6")
-        switch.add_link(tgen.gears["rt6"], nodeif="eth-rt4")
+    switch = tgen.add_switch("s8")
+    switch.add_link(tgen.gears["rt5"], nodeif="eth-rt6")
+    switch.add_link(tgen.gears["rt6"], nodeif="eth-rt5")
 
-        switch = tgen.add_switch("s8")
-        switch.add_link(tgen.gears["rt5"], nodeif="eth-rt6")
-        switch.add_link(tgen.gears["rt6"], nodeif="eth-rt5")
-
-        switch = tgen.add_switch("s9")
-        switch.add_link(tgen.gears["rt6"], nodeif="eth-dst")
-        switch.add_link(tgen.gears["dst"], nodeif="eth-rt6")
+    switch = tgen.add_switch("s9")
+    switch.add_link(tgen.gears["rt6"], nodeif="eth-dst")
+    switch.add_link(tgen.gears["dst"], nodeif="eth-rt6")
 
 
 def setup_module(mod):
     "Sets up the pytest environment"
 
-    tgen = Topogen(TemplateTopo, mod.__name__)
+    tgen = Topogen(build_topo, mod.__name__)
 
     frrdir = tgen.config.get(tgen.CONFIG_SECTION, "frrdir")
     if not os.path.isfile(os.path.join(frrdir, "pathd")):
@@ -167,7 +147,7 @@ def setup_module(mod):
     router_list = tgen.routers()
 
     # For all registered routers, load the zebra configuration file
-    for rname, router in router_list.iteritems():
+    for rname, router in router_list.items():
         router.load_config(
             TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
         )
@@ -239,13 +219,52 @@ def cmp_json_output_exact(rname, command, reference):
     return cmp_json_output(rname, command, reference, True)
 
 
-def add_candidate_path(rname, endpoint, pref, name, segment_list="default"):
+def compare_json_test_inverted(router, command, reference, exact):
+    "logically inverts result of compare_json_test"
+
+    # None vs something else
+    result = compare_json_test(router, command, reference, exact)
+    if result is None:
+        return "Some"
+    return None
+
+
+def cmp_json_output_doesnt(rname, command, reference):
+    "Compare router JSON output, shouldn't include reference"
+
+    logger.info('Comparing (anti) router "%s" "%s" output', rname, command)
+
+    tgen = get_topogen()
+    filename = "{}/{}/{}".format(CWD, rname, reference)
+    expected = json.loads(open(filename).read())
+
+    # Run test function until we get an result. Wait at most 60 seconds.
+    test_func = partial(
+        compare_json_test_inverted, tgen.gears[rname], command, expected, exact=False
+    )
+    _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5)
+    assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+    assert diff is None, assertmsg
+
+
+def dump_json(v):
+    if isinstance(v, (dict, list)):
+        return "\t" + "\t".join(
+            json.dumps(v, indent=4, separators=(",", ": ")).splitlines(True)
+        )
+    else:
+        return "'{}'".format(v)
+
+
+def add_candidate_path(rname, endpoint, pref, name, segment_list="default", color=1):
     get_topogen().net[rname].cmd(
         """ \
         vtysh -c "conf t" \
               -c "segment-routing" \
               -c "traffic-eng" \
-              -c "policy color 1 endpoint """
+              -c "policy color """
+        + str(color)
+        + " endpoint "
         + endpoint
         + """" \
               -c "candidate-path preference """
@@ -258,13 +277,15 @@ def add_candidate_path(rname, endpoint, pref, name, segment_list="default"):
     )
 
 
-def delete_candidate_path(rname, endpoint, pref):
+def delete_candidate_path(rname, endpoint, pref, color=1):
     get_topogen().net[rname].cmd(
         """ \
         vtysh -c "conf t" \
               -c "segment-routing" \
               -c "traffic-eng" \
-              -c "policy color 1 endpoint """
+              -c "policy color """
+        + str(color)
+        + " endpoint "
         + endpoint
         + """" \
               -c "no candidate-path preference """
@@ -356,6 +377,50 @@ def delete_prefix_sid(rname, prefix):
     )
 
 
+def set_route_map_color(rname, color):
+    get_topogen().net[rname].cmd(
+        ''' \
+        vtysh -c "conf t" \
+              -c "route-map SET_SR_POLICY permit 10" \
+              -c "set sr-te color  "'''
+        + str(color)
+    )
+
+
+def router_bgp_shutdown_neighbor(rname, neighbor):
+    get_topogen().net[rname].cmd(
+        """ \
+        vtysh -c "conf t" \
+              -c "router bgp 1" \
+              -c " neighbor """
+        + neighbor
+        + ' shutdown"'
+    )
+
+
+def router_bgp_no_shutdown_neighbor(rname, neighbor):
+    get_topogen().net[rname].cmd(
+        """ \
+        vtysh -c "conf t" \
+              -c "router bgp 1" \
+              -c " no neighbor """
+        + neighbor
+        + ' shutdown"'
+    )
+
+
+def show_running_cfg(rname):
+    output = (
+        get_topogen()
+        .net[rname]
+        .cmd(
+            """ \
+        vtysh -c "show run" """
+        )
+    )
+    logger.info(output)
+
+
 #
 # Step 1
 #
@@ -549,6 +614,22 @@ def test_srte_segment_list_add_segment_check_mpls_table_step4():
     delete_candidate_path("rt1", "6.6.6.6", 100)
 
 
+def save_rt(routername, filename):
+    save_filename = routername + "/" + filename
+    tgen = get_topogen()
+    router = tgen.gears[routername]
+
+    config_output = router.vtysh_cmd("sh run")
+
+    route_output_json = json.loads(router.vtysh_cmd("show ip route bgp json"))
+    route_output = dump_json(route_output_json)
+
+    f = open(save_filename, "w")
+    f.write(config_output)
+    f.write(route_output)
+    f.close()
+
+
 #
 # Step 5
 #
@@ -583,6 +664,147 @@ def test_srte_route_map_with_sr_policy_check_nextop_step5():
         delete_candidate_path("rt1", "6.6.6.6", 100)
 
 
+def test_srte_route_map_sr_policy_vs_route_order_step5():
+    setup_testcase(
+        "Test (step 5): Config policy first, add route after and check route validity"
+    )
+
+    #
+    # BGP route and route-map are already configured.
+    # route-map sets color 1 on BGP routes
+
+    # Developer: to force pause here
+    # tgen = get_topogen()
+    # tgen.mininet_cli()
+
+    #
+    # Configure policy/path
+    #
+    add_candidate_path("rt1", "6.6.6.6", 100, "default", "default", 1)
+
+    #
+    # Route should be valid
+    #
+    logger.info(
+        "BGP route and route-map are already configured, SR candidate path added after. Route should be valid"
+    )
+    cmp_json_output(
+        "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_active_srte.ref"
+    )
+
+    #
+    # shutdown/no-shutdown on BGP neighbor to delete/re-add BGP route
+    #
+    router_bgp_shutdown_neighbor("rt1", "6.6.6.6")
+    router_bgp_no_shutdown_neighbor("rt1", "6.6.6.6")
+
+    #
+    # Route should be valid (but isn't)
+    #
+    logger.info(
+        "After shutdown + no-shutdown neighbor. Route should be valid, but isn't"
+    )
+    cmp_json_output(
+        "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_active_srte.ref"
+    )
+
+    #
+    # delete and re-add policy/path
+    #
+    delete_candidate_path("rt1", "6.6.6.6", 100, 1)
+    add_candidate_path("rt1", "6.6.6.6", 100, "default", "default", 1)
+
+    #
+    # Route should be valid
+    #
+    logger.info("After re-add candidate path. Route should be valid")
+    cmp_json_output(
+        "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_active_srte.ref"
+    )
+
+    # Developer: to force pause here
+    # tgen = get_topogen()
+    # tgen.mininet_cli()
+
+    # clean up
+    delete_candidate_path("rt1", "6.6.6.6", 100, 2)
+
+
+def test_srte_route_map_sr_policy_vs_routemap_order_step5():
+    setup_testcase(
+        "Test (step 5): Config policy first, set route-map after and check route validity"
+    )
+
+    #
+    # BGP route and route-map are already configured.
+    # route-map sets color 1 on BGP routes
+    #
+
+    #
+    # Configure policy/path
+    #
+    add_candidate_path("rt1", "6.6.6.6", 100, "default", "default", 1)
+
+    #
+    # Route should be valid
+    #
+    logger.info("After add candidate path. Route should be valid")
+    cmp_json_output(
+        "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_active_srte.ref"
+    )
+
+    # Developer: to force pause here
+    # tgen = get_topogen()
+    # tgen.mininet_cli()
+
+    #
+    # change route-map color to someting else and back again
+    #
+    set_route_map_color("rt1", 2)
+    logger.info("route-map color was set to 2")
+    # show_running_cfg("rt1")
+    # 220625 nexthop no longer becomes empty. Colored routes without
+    # matching SR policies now fall back to their non-colored equivalent
+    # nexthops. So the route to 9.9.9.9/32 will now be valid, but with
+    # different nexthop values.
+    logger.info("now route table will lose policy-mapped route")
+    cmp_json_output_doesnt(
+        "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_active_srte.ref"
+    )
+    set_route_map_color("rt1", 1)
+    logger.info("route-map color was set to 1")
+    # show_running_cfg("rt1")
+
+    #
+    # Route should be valid (but isn't)
+    #
+    logger.info("After change route-map color. Route should be valid, but isn't")
+    cmp_json_output(
+        "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_active_srte.ref"
+    )
+
+    #
+    # delete and re-add policy/path
+    #
+    delete_candidate_path("rt1", "6.6.6.6", 100, 1)
+    add_candidate_path("rt1", "6.6.6.6", 100, "default", "default", 1)
+
+    #
+    # Route should be valid
+    #
+    logger.info("After delete/re-add candidate path. Route should be valid")
+    cmp_json_output(
+        "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_active_srte.ref"
+    )
+
+    # Developer: force pause
+    # tgen = get_topogen()
+    # tgen.mininet_cli()
+
+    # clean up
+    delete_candidate_path("rt1", "6.6.6.6", 100, 2)
+
+
 def test_srte_route_map_with_sr_policy_reinstall_prefix_sid_check_nextop_step5():
     setup_testcase(
         "Test (step 5): remove and re-install prefix SID on fist path element and check SR Policy activity"