frr/tests/topotests/ldp-sync-isis-topo1/test_ldp_sync_isis_topo1.py
whitespace 701a01920e *: reformat python files
We are now using black.

Signed-off-by: Quentin Young <qlyoung@nvidia.com>
2020-10-07 17:22:26 -04:00

631 lines
19 KiB
Python

#!/usr/bin/env python
#
# test_ldp_isis_topo1.py
# Part of NetDEF Topology Tests
#
# Copyright (c) 2020 by Volta Networks
#
# 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_ldp_vpls_topo1.py:
+---------+ +---------+
| | | |
| CE1 | | CE2 |
| | | |
+---------+ +---------+
ce1-eth0 (172.16.1.1/24)| |ce2-eth0 (172.16.1.2/24)
| |
| |
rt1-eth0| |rt2-eth0
+---------+ 10.0.1.0/24 +---------+
| |rt1-eth1 | |
| RT1 +----------------+ RT2 |
| 1.1.1.1 | rt2-eth1| 2.2.2.2 |
| | | |
+---------+ +---------+
rt1-eth2| |rt2-eth2
| |
| |
10.0.2.0/24| +---------+ |10.0.3.0/24
| | | |
| | RT3 | |
+--------+ 3.3.3.3 +-------+
rt3-eth2| |rt3-eth1
+---------+
|rt3-eth0
|
|
ce3-eth0 (172.16.1.3/24)|
+---------+
| |
| CE3 |
| |
+---------+
"""
import os
import re
import sys
import pytest
import json
from time import sleep
from functools import partial
# Save the Current Working Directory to find configuration files.
CWD = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(CWD, "../"))
# pylint: disable=C0413
# Import topogen and topotest helpers
from lib import topotest
from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.topolog import logger
# Required to instantiate the topology builder class.
from mininet.topo import Topo
class TemplateTopo(Topo):
"Test topology builder"
def build(self, *_args, **_opts):
"Build function"
tgen = get_topogen(self)
#
# Define FRR Routers
#
for router in ["ce1", "ce2", "ce3", "r1", "r2", "r3"]:
tgen.add_router(router)
#
# Define connections
#
switch = tgen.add_switch("s1")
switch.add_link(tgen.gears["ce1"])
switch.add_link(tgen.gears["r1"])
switch = tgen.add_switch("s2")
switch.add_link(tgen.gears["ce2"])
switch.add_link(tgen.gears["r2"])
switch = tgen.add_switch("s3")
switch.add_link(tgen.gears["ce3"])
switch.add_link(tgen.gears["r3"])
switch = tgen.add_switch("s4")
switch.add_link(tgen.gears["r1"])
switch.add_link(tgen.gears["r2"])
switch = tgen.add_switch("s5")
switch.add_link(tgen.gears["r1"])
switch.add_link(tgen.gears["r3"])
switch = tgen.add_switch("s6")
switch.add_link(tgen.gears["r2"])
switch.add_link(tgen.gears["r3"])
def setup_module(mod):
"Sets up the pytest environment"
tgen = Topogen(TemplateTopo, mod.__name__)
tgen.start_topology()
router_list = tgen.routers()
# For all registered routers, load the zebra configuration file
for rname, router in router_list.items():
router.load_config(
TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
)
# Don't start isisd and ldpd in the CE nodes
if router.name[0] == "r":
router.load_config(
TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))
)
router.load_config(
TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname))
)
tgen.start_router()
def teardown_module(mod):
"Teardown the pytest environment"
tgen = get_topogen()
# This function tears down the whole topology.
tgen.stop_topology()
def router_compare_json_output(rname, command, reference):
"Compare router JSON output"
logger.info('Comparing router "%s" "%s" output', rname, command)
tgen = get_topogen()
filename = "{}/{}/{}".format(CWD, rname, reference)
expected = json.loads(open(filename).read())
# Run test function until we get an result.
test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
_, diff = topotest.run_and_expect(test_func, None, count=320, wait=0.5)
assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
assert diff is None, assertmsg
def test_isis_convergence():
logger.info("Test: check ISIS adjacencies")
tgen = get_topogen()
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
for rname in ["r1", "r2", "r3"]:
router_compare_json_output(
rname,
"show yang operational-data /frr-interface:lib isisd",
"show_yang_interface_isis_adjacencies.ref",
)
def test_rib():
logger.info("Test: verify RIB")
tgen = get_topogen()
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
for rname in ["r1", "r2", "r3"]:
router_compare_json_output(rname, "show ip route json", "show_ip_route.ref")
def test_ldp_adjacencies():
logger.info("Test: verify LDP adjacencies")
tgen = get_topogen()
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
for rname in ["r1", "r2", "r3"]:
router_compare_json_output(
rname, "show mpls ldp discovery json", "show_ldp_discovery.ref"
)
def test_ldp_neighbors():
logger.info("Test: verify LDP neighbors")
tgen = get_topogen()
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
for rname in ["r1", "r2", "r3"]:
router_compare_json_output(
rname, "show mpls ldp neighbor json", "show_ldp_neighbor.ref"
)
def test_ldp_bindings():
logger.info("Test: verify LDP bindings")
tgen = get_topogen()
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
for rname in ["r1", "r2", "r3"]:
router_compare_json_output(
rname, "show mpls ldp binding json", "show_ldp_binding.ref"
)
def test_ldp_pwid_bindings():
logger.info("Test: verify LDP PW-ID bindings")
tgen = get_topogen()
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
for rname in ["r1", "r2", "r3"]:
router_compare_json_output(
rname, "show l2vpn atom binding json", "show_l2vpn_binding.ref"
)
def test_ldp_pseudowires():
logger.info("Test: verify LDP pseudowires")
tgen = get_topogen()
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
for rname in ["r1", "r2", "r3"]:
router_compare_json_output(
rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref"
)
def test_ldp_igp_sync():
logger.info("Test: verify LDP igp-sync")
tgen = get_topogen()
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
for rname in ["r1", "r2", "r3"]:
router_compare_json_output(
rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync.ref"
)
def test_isis_ldp_sync():
logger.info("Test: verify ISIS igp-sync")
tgen = get_topogen()
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
for rname in ["r1", "r2", "r3"]:
(result, diff) = validate_show_isis_ldp_sync(rname, "show_isis_ldp_sync.ref")
assert result, "ISIS did not converge on {}:\n{}".format(rname, diff)
for rname in ["r1", "r2", "r3"]:
(result, diff) = validate_show_isis_interface_detail(
rname, "show_isis_interface_detail.ref"
)
assert result, "ISIS interface did not converge on {}:\n{}".format(rname, diff)
def test_r1_eth1_shutdown():
logger.info("Test: verify behaviour after r1-eth1 is shutdown")
tgen = get_topogen()
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
# Shut down r1-r2 link */
tgen = get_topogen()
tgen.gears["r1"].peer_link_enable("r1-eth1", False)
topotest.sleep(5, "Waiting for the network to reconverge")
# check if the pseudowire is still up (using an alternate path for nexthop resolution)
for rname in ["r1", "r2", "r3"]:
router_compare_json_output(
rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref"
)
for rname in ["r1", "r2", "r3"]:
router_compare_json_output(
rname,
"show mpls ldp igp-sync json",
"show_ldp_igp_sync_r1_eth1_shutdown.ref",
)
for rname in ["r1", "r2", "r3"]:
(result, diff) = validate_show_isis_ldp_sync(
rname, "show_isis_ldp_sync_r1_eth1_shutdown.ref"
)
assert result, "ISIS did not converge on {}:\n{}".format(rname, diff)
for rname in ["r1", "r2", "r3"]:
(result, diff) = validate_show_isis_interface_detail(
rname, "show_isis_interface_detail_r1_eth1_shutdown.ref"
)
assert result, "ISIS interface did not converge on {}:\n{}".format(rname, diff)
def test_r1_eth1_no_shutdown():
logger.info("Test: verify behaviour after r1-eth1 is no shutdown")
tgen = get_topogen()
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
# Run no shutdown on r1-eth1 interface */
tgen = get_topogen()
tgen.gears["r1"].peer_link_enable("r1-eth1", True)
topotest.sleep(5, "Waiting for the network to reconverge")
for rname in ["r1", "r2", "r3"]:
router_compare_json_output(
rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync.ref"
)
for rname in ["r1", "r2", "r3"]:
(result, diff) = validate_show_isis_ldp_sync(rname, "show_isis_ldp_sync.ref")
assert result, "ISIS did not converge on {}:\n{}".format(rname, diff)
for rname in ["r1", "r2", "r3"]:
(result, diff) = validate_show_isis_interface_detail(
rname, "show_isis_interface_detail.ref"
)
assert result, "ISIS interface did not converge on {}:\n{}".format(rname, diff)
def test_r2_eth1_shutdown():
logger.info("Test: verify behaviour after r2-eth1 is shutdown")
tgen = get_topogen()
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
# Shut down r1-r2 link */
tgen = get_topogen()
tgen.gears["r2"].peer_link_enable("r2-eth1", False)
topotest.sleep(5, "Waiting for the network to reconverge")
for rname in ["r1", "r2", "r3"]:
router_compare_json_output(
rname,
"show mpls ldp igp-sync json",
"show_ldp_igp_sync_r1_eth1_shutdown.ref",
)
for rname in ["r1", "r2", "r3"]:
(result, diff) = validate_show_isis_ldp_sync(
rname, "show_isis_ldp_sync_r2_eth1_shutdown.ref"
)
assert result, "ISIS did not converge on {}:\n{}".format(rname, diff)
for rname in ["r1", "r2", "r3"]:
(result, diff) = validate_show_isis_interface_detail(
rname, "show_isis_interface_detail_r2_eth1_shutdown.ref"
)
assert result, "ISIS interface did not converge on {}:\n{}".format(rname, diff)
def test_r2_eth1_no_shutdown():
logger.info("Test: verify behaviour after r2-eth1 is no shutdown")
tgen = get_topogen()
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
# Run no shutdown on r2-eth1 interface */
tgen = get_topogen()
tgen.gears["r2"].peer_link_enable("r2-eth1", True)
topotest.sleep(5, "Waiting for the network to reconverge")
for rname in ["r1", "r2", "r3"]:
router_compare_json_output(
rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync.ref"
)
for rname in ["r1", "r2", "r3"]:
(result, diff) = validate_show_isis_ldp_sync(rname, "show_isis_ldp_sync.ref")
assert result, "ISIS did not converge on {}:\n{}".format(rname, diff)
for rname in ["r1", "r2", "r3"]:
(result, diff) = validate_show_isis_interface_detail(
rname, "show_isis_interface_detail.ref"
)
assert result, "ISIS interface did not converge on {}:\n{}".format(rname, diff)
# Memory leak test template
def test_memory_leak():
"Run the memory leak test and report results."
tgen = get_topogen()
if not tgen.is_memleak_enabled():
pytest.skip("Memory leak test/report is disabled")
tgen.report_memory_leaks()
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))
#
# Auxiliary functions
#
def parse_show_isis_ldp_sync(lines, rname):
"""
Parse the output of 'show isis mpls ldp sync' into a Python dict.
"""
interfaces = {}
it = iter(lines)
while True:
try:
interface = {}
interface_name = None
line = it.next()
if line.startswith(rname + "-eth"):
interface_name = line
line = it.next()
if line.startswith(" LDP-IGP Synchronization enabled: "):
interface["ldpIgpSyncEnabled"] = line.endswith("yes")
line = it.next()
if line.startswith(" holddown timer in seconds: "):
interface["holdDownTimeInSec"] = int(line.split(": ")[-1])
line = it.next()
if line.startswith(" State: "):
interface["ldpIgpSyncState"] = line.split(": ")[-1]
elif line.startswith(" Interface "):
interface["Interface"] = line.endswith("down")
interfaces[interface_name] = interface
except StopIteration:
break
return interfaces
def show_isis_ldp_sync(router, rname):
"""
Get the show isis mpls ldp-sync info in a dictionary format.
"""
out = topotest.normalize_text(
router.vtysh_cmd("show isis mpls ldp-sync")
).splitlines()
parsed = parse_show_isis_ldp_sync(out, rname)
return parsed
def validate_show_isis_ldp_sync(rname, fname):
tgen = get_topogen()
filename = "{0}/{1}/{2}".format(CWD, rname, fname)
expected = json.loads(open(filename).read())
router = tgen.gears[rname]
def compare_isis_ldp_sync(router, expected):
"Helper function to test show isis mpls ldp-sync"
actual = show_isis_ldp_sync(router, rname)
return topotest.json_cmp(actual, expected)
test_func = partial(compare_isis_ldp_sync, router, expected)
(result, diff) = topotest.run_and_expect(test_func, None, wait=0.5, count=160)
return (result, diff)
def parse_show_isis_interface_detail(lines, rname):
"""
Parse the output of 'show isis interface detail' into a Python dict.
"""
areas = {}
area_id = None
it = iter(lines)
while True:
try:
line = it.next()
area_match = re.match(r"Area (.+):", line)
if not area_match:
continue
area_id = area_match.group(1)
area = {}
line = it.next()
while line.startswith(" Interface: "):
interface_name = re.split(":|,", line)[1].lstrip()
area[interface_name] = []
# Look for keyword: Level-1 or Level-2
while not line.startswith(" Level-"):
line = it.next()
while line.startswith(" Level-"):
level = {}
level_name = line.split()[0]
level["level"] = level_name
line = it.next()
if line.startswith(" Metric:"):
level["metric"] = re.split(":|,", line)[1].lstrip()
area[interface_name].append(level)
# Look for keyword: Level-1 or Level-2 or Interface:
while not line.startswith(" Level-") and not line.startswith(
" Interface: "
):
line = it.next()
if line.startswith(" Level-"):
continue
if line.startswith(" Interface: "):
break
areas[area_id] = area
except StopIteration:
areas[area_id] = area
break
return areas
def show_isis_interface_detail(router, rname):
"""
Get the show isis mpls ldp-sync info in a dictionary format.
"""
out = topotest.normalize_text(
router.vtysh_cmd("show isis interface detail")
).splitlines()
logger.warning(out)
parsed = parse_show_isis_interface_detail(out, rname)
logger.warning(parsed)
return parsed
def validate_show_isis_interface_detail(rname, fname):
tgen = get_topogen()
filename = "{0}/{1}/{2}".format(CWD, rname, fname)
expected = json.loads(open(filename).read())
router = tgen.gears[rname]
def compare_isis_interface_detail(router, expected):
"Helper function to test show isis interface detail"
actual = show_isis_interface_detail(router, rname)
return topotest.json_cmp(actual, expected)
test_func = partial(compare_isis_interface_detail, router, expected)
(result, diff) = topotest.run_and_expect(test_func, None, wait=0.5, count=160)
return (result, diff)