2017-04-08 12:49:43 +02:00
|
|
|
#!/usr/bin/env python
|
2023-02-08 13:17:09 +01:00
|
|
|
# SPDX-License-Identifier: ISC
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
#
|
|
|
|
# test_all_protocol_startup.py
|
|
|
|
# Part of NetDEF Topology Tests
|
|
|
|
#
|
|
|
|
# Copyright (c) 2017 by
|
|
|
|
# Network Device Education Foundation, Inc. ("NetDEF")
|
|
|
|
#
|
|
|
|
|
|
|
|
"""
|
|
|
|
test_all_protocol_startup.py: Test of all protocols at same time
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
import os
|
|
|
|
import re
|
|
|
|
import sys
|
|
|
|
import pytest
|
2018-04-21 19:23:54 +02:00
|
|
|
import glob
|
2017-04-08 12:49:43 +02:00
|
|
|
from time import sleep
|
2024-09-20 03:06:40 +02:00
|
|
|
from lib.topolog import logger
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2021-01-23 21:52:44 +01:00
|
|
|
pytestmark = [
|
|
|
|
pytest.mark.babeld,
|
|
|
|
pytest.mark.bgpd,
|
|
|
|
pytest.mark.isisd,
|
|
|
|
pytest.mark.nhrpd,
|
|
|
|
pytest.mark.ospfd,
|
|
|
|
pytest.mark.pbrd,
|
|
|
|
pytest.mark.ripd,
|
|
|
|
]
|
2021-01-23 21:13:12 +01:00
|
|
|
|
2017-04-08 12:49:43 +02:00
|
|
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
from lib import topotest
|
2021-07-29 11:38:55 +02:00
|
|
|
from lib.topogen import Topogen, get_topogen
|
2022-10-26 21:47:08 +02:00
|
|
|
from lib.common_config import (
|
|
|
|
required_linux_kernel_version,
|
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2023-10-05 19:47:17 +02:00
|
|
|
import json
|
2024-08-14 14:37:00 +02:00
|
|
|
import functools
|
2023-10-05 19:47:17 +02:00
|
|
|
|
2024-08-14 14:37:00 +02:00
|
|
|
# Global that must be set on a failure to stop subsequent tests from being run
|
2017-04-08 12:49:43 +02:00
|
|
|
fatal_error = ""
|
|
|
|
|
|
|
|
|
|
|
|
#####################################################
|
|
|
|
##
|
|
|
|
## Network Topology Definition
|
|
|
|
##
|
|
|
|
#####################################################
|
|
|
|
|
2020-10-07 23:22:26 +02:00
|
|
|
|
2021-07-29 11:38:55 +02:00
|
|
|
def build_topo(tgen):
|
|
|
|
router = tgen.add_router("r1")
|
|
|
|
for i in range(0, 10):
|
2024-05-27 08:50:25 +02:00
|
|
|
tgen.add_switch("sw{}".format(i)).add_link(router)
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
|
|
|
|
#####################################################
|
|
|
|
##
|
|
|
|
## Tests starting
|
|
|
|
##
|
|
|
|
#####################################################
|
|
|
|
|
2021-01-29 21:58:34 +01:00
|
|
|
|
2017-04-08 12:49:43 +02:00
|
|
|
def setup_module(module):
|
|
|
|
global fatal_error
|
|
|
|
|
2024-05-27 08:50:25 +02:00
|
|
|
print("\n\n** {}: Setup Topology".format(module.__name__))
|
2017-04-08 12:49:43 +02:00
|
|
|
print("******************************************\n")
|
|
|
|
|
|
|
|
thisDir = os.path.dirname(os.path.realpath(__file__))
|
2021-07-29 11:38:55 +02:00
|
|
|
tgen = Topogen(build_topo, module.__name__)
|
|
|
|
tgen.start_topology()
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2021-07-29 11:38:55 +02:00
|
|
|
net = tgen.net
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
if net["r1"].get_routertype() != "frr":
|
|
|
|
fatal_error = "Test is only implemented for FRR"
|
|
|
|
sys.stderr.write("\n\nTest is only implemented for FRR - Skipping\n\n")
|
|
|
|
pytest.skip(fatal_error)
|
2020-10-07 23:22:26 +02:00
|
|
|
|
2017-04-08 12:49:43 +02:00
|
|
|
# Starting Routers
|
|
|
|
#
|
|
|
|
# Main router
|
|
|
|
for i in range(1, 2):
|
2024-05-27 08:50:25 +02:00
|
|
|
net["r{}".format(i)].loadConf("mgmtd", "{}/r{}/zebra.conf".format(thisDir, i))
|
|
|
|
net["r{}".format(i)].loadConf("zebra", "{}/r{}/zebra.conf".format(thisDir, i))
|
|
|
|
net["r{}".format(i)].loadConf("ripd", "{}/r{}/ripd.conf".format(thisDir, i))
|
|
|
|
net["r{}".format(i)].loadConf("ripngd", "{}/r{}/ripngd.conf".format(thisDir, i))
|
|
|
|
net["r{}".format(i)].loadConf("ospfd", "{}/r{}/ospfd.conf".format(thisDir, i))
|
2018-09-07 02:15:19 +02:00
|
|
|
if net["r1"].checkRouterVersion("<", "4.0"):
|
2024-05-27 08:50:25 +02:00
|
|
|
net["r{}".format(i)].loadConf(
|
|
|
|
"ospf6d", "{}/r{}/ospf6d.conf-pre-v4".format(thisDir, i)
|
2020-10-07 23:22:26 +02:00
|
|
|
)
|
2020-09-23 23:08:36 +02:00
|
|
|
else:
|
2024-05-27 08:50:25 +02:00
|
|
|
net["r{}".format(i)].loadConf(
|
|
|
|
"ospf6d", "{}/r{}/ospf6d.conf".format(thisDir, i)
|
|
|
|
)
|
|
|
|
net["r{}".format(i)].loadConf("isisd", "{}/r{}/isisd.conf".format(thisDir, i))
|
|
|
|
net["r{}".format(i)].loadConf("bgpd", "{}/r{}/bgpd.conf".format(thisDir, i))
|
|
|
|
if net["r{}".format(i)].daemon_available("ldpd"):
|
2017-04-08 12:49:43 +02:00
|
|
|
# Only test LDPd if it's installed and Kernel >= 4.5
|
2024-05-27 08:50:25 +02:00
|
|
|
net["r{}".format(i)].loadConf("ldpd", "{}/r{}/ldpd.conf".format(thisDir, i))
|
|
|
|
net["r{}".format(i)].loadConf("sharpd")
|
|
|
|
net["r{}".format(i)].loadConf("nhrpd", "{}/r{}/nhrpd.conf".format(thisDir, i))
|
|
|
|
net["r{}".format(i)].loadConf("babeld", "{}/r{}/babeld.conf".format(thisDir, i))
|
|
|
|
net["r{}".format(i)].loadConf("pbrd", "{}/r{}/pbrd.conf".format(thisDir, i))
|
|
|
|
tgen.gears["r{}".format(i)].start()
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2020-09-19 03:07:20 +02:00
|
|
|
# For debugging after starting FRR daemons, uncomment the next line
|
2021-10-06 19:49:52 +02:00
|
|
|
# tgen.mininet_cli()
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
|
|
|
|
def teardown_module(module):
|
2024-05-27 08:50:25 +02:00
|
|
|
print("\n\n** {}: Shutdown Topology".format(module.__name__))
|
2017-04-08 12:49:43 +02:00
|
|
|
print("******************************************\n")
|
2021-07-29 11:38:55 +02:00
|
|
|
tgen = get_topogen()
|
|
|
|
tgen.stop_topology()
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_router_running():
|
|
|
|
global fatal_error
|
2021-10-06 19:49:52 +02:00
|
|
|
tgen = get_topogen()
|
|
|
|
net = tgen.net
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
# Skip if previous fatal error condition is raised
|
|
|
|
if fatal_error != "":
|
|
|
|
pytest.skip(fatal_error)
|
|
|
|
|
2020-09-19 03:07:20 +02:00
|
|
|
print("\n\n** Check if FRR is running on each Router node")
|
2017-04-08 12:49:43 +02:00
|
|
|
print("******************************************\n")
|
|
|
|
sleep(5)
|
|
|
|
|
|
|
|
# Starting Routers
|
|
|
|
for i in range(1, 2):
|
2024-05-27 08:50:25 +02:00
|
|
|
fatal_error = net["r{}".format(i)].checkRouterRunning()
|
2017-04-08 12:49:43 +02:00
|
|
|
assert fatal_error == "", fatal_error
|
|
|
|
|
2020-09-19 03:07:20 +02:00
|
|
|
# For debugging after starting FRR daemons, uncomment the next line
|
2021-10-06 19:49:52 +02:00
|
|
|
# tgen.mininet_cli()
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_error_messages_vtysh():
|
|
|
|
global fatal_error
|
2021-07-29 11:38:55 +02:00
|
|
|
net = get_topogen().net
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
# Skip if previous fatal error condition is raised
|
|
|
|
if fatal_error != "":
|
|
|
|
pytest.skip(fatal_error)
|
|
|
|
|
|
|
|
print("\n\n** Check for error messages on VTYSH")
|
|
|
|
print("******************************************\n")
|
|
|
|
|
|
|
|
for i in range(1, 2):
|
|
|
|
#
|
|
|
|
# First checking Standard Output
|
|
|
|
#
|
|
|
|
|
|
|
|
# VTYSH output from router
|
2024-05-27 08:50:25 +02:00
|
|
|
vtystdout = (
|
|
|
|
net["r{}".format(i)].cmd('vtysh -c "show version" 2> /dev/null').rstrip()
|
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
# Fix newlines (make them all the same)
|
|
|
|
vtystdout = ("\n".join(vtystdout.splitlines()) + "\n").rstrip()
|
|
|
|
# Drop everything starting with "FRRouting X.xx" message
|
|
|
|
vtystdout = re.sub(r"FRRouting [0-9]+.*", "", vtystdout, flags=re.DOTALL)
|
|
|
|
|
2017-08-31 02:21:29 +02:00
|
|
|
if vtystdout == "":
|
2024-05-27 08:50:25 +02:00
|
|
|
print("r{} StdOut ok".format(i))
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2024-05-27 08:50:25 +02:00
|
|
|
assert (
|
|
|
|
vtystdout == ""
|
|
|
|
), "Vtysh StdOut Output check failed for router r{}".format(i)
|
2017-04-25 01:12:55 +02:00
|
|
|
|
2017-04-08 12:49:43 +02:00
|
|
|
#
|
|
|
|
# Second checking Standard Error
|
|
|
|
#
|
|
|
|
|
|
|
|
# VTYSH StdErr output from router
|
2024-05-27 08:50:25 +02:00
|
|
|
vtystderr = (
|
|
|
|
net["r{}".format(i)].cmd('vtysh -c "show version" > /dev/null').rstrip()
|
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
# Fix newlines (make them all the same)
|
|
|
|
vtystderr = ("\n".join(vtystderr.splitlines()) + "\n").rstrip()
|
|
|
|
# # Drop everything starting with "FRRouting X.xx" message
|
|
|
|
# vtystderr = re.sub(r"FRRouting [0-9]+.*", "", vtystderr, flags=re.DOTALL)
|
|
|
|
|
2017-08-31 02:21:29 +02:00
|
|
|
if vtystderr == "":
|
2024-05-27 08:50:25 +02:00
|
|
|
print("r{} StdErr ok".format(i))
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2024-05-27 08:50:25 +02:00
|
|
|
assert (
|
|
|
|
vtystderr == ""
|
|
|
|
), "Vtysh StdErr Output check failed for router r{}".format(i)
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2017-05-20 05:41:20 +02:00
|
|
|
# Make sure that all daemons are running
|
|
|
|
for i in range(1, 2):
|
2024-05-27 08:50:25 +02:00
|
|
|
fatal_error = net["r{}".format(i)].checkRouterRunning()
|
2017-05-20 05:41:20 +02:00
|
|
|
assert fatal_error == "", fatal_error
|
|
|
|
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
def test_error_messages_daemons():
|
|
|
|
global fatal_error
|
2021-07-29 11:38:55 +02:00
|
|
|
net = get_topogen().net
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
# Skip if previous fatal error condition is raised
|
|
|
|
if fatal_error != "":
|
|
|
|
pytest.skip(fatal_error)
|
|
|
|
|
2022-10-10 18:42:45 +02:00
|
|
|
if os.environ.get("TOPOTESTS_CHECK_STDERR") is None:
|
|
|
|
print(
|
|
|
|
"SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n"
|
|
|
|
)
|
|
|
|
pytest.skip("Skipping test for Stderr output")
|
|
|
|
|
2017-04-08 12:49:43 +02:00
|
|
|
print("\n\n** Check for error messages in daemons")
|
|
|
|
print("******************************************\n")
|
|
|
|
|
|
|
|
error_logs = ""
|
|
|
|
|
|
|
|
for i in range(1, 2):
|
2024-05-27 08:50:25 +02:00
|
|
|
log = net["r{}".format(i)].getStdErr("ripd")
|
2017-04-08 12:49:43 +02:00
|
|
|
if log:
|
2024-05-27 08:50:25 +02:00
|
|
|
error_logs += "r{} RIPd StdErr Output:\n".format(i)
|
2017-04-08 12:49:43 +02:00
|
|
|
error_logs += log
|
2024-05-27 08:50:25 +02:00
|
|
|
log = net["r{}".format(i)].getStdErr("ripngd")
|
2017-04-08 12:49:43 +02:00
|
|
|
if log:
|
2024-05-27 08:50:25 +02:00
|
|
|
error_logs += "r{} RIPngd StdErr Output:\n".format(i)
|
2017-04-08 12:49:43 +02:00
|
|
|
error_logs += log
|
2024-05-27 08:50:25 +02:00
|
|
|
log = net["r{}".format(i)].getStdErr("ospfd")
|
2017-04-08 12:49:43 +02:00
|
|
|
if log:
|
2024-05-27 08:50:25 +02:00
|
|
|
error_logs += "r{} OSPFd StdErr Output:\n".format(i)
|
2017-04-08 12:49:43 +02:00
|
|
|
error_logs += log
|
2024-05-27 08:50:25 +02:00
|
|
|
log = net["r{}".format(i)].getStdErr("ospf6d")
|
2017-04-08 12:49:43 +02:00
|
|
|
if log:
|
2024-05-27 08:50:25 +02:00
|
|
|
error_logs += "r{} OSPF6d StdErr Output:\n".format(i)
|
2017-04-08 12:49:43 +02:00
|
|
|
error_logs += log
|
2024-05-27 08:50:25 +02:00
|
|
|
log = net["r{}".format(i)].getStdErr("isisd")
|
2017-04-08 12:49:43 +02:00
|
|
|
# ISIS shows debugging enabled status on StdErr
|
|
|
|
# Remove these messages
|
|
|
|
log = re.sub(r"^IS-IS .* debugging is on.*", "", log).rstrip()
|
|
|
|
if log:
|
2024-05-27 08:50:25 +02:00
|
|
|
error_logs += "r{} ISISd StdErr Output:\n".format(i)
|
2017-04-08 12:49:43 +02:00
|
|
|
error_logs += log
|
2024-05-27 08:50:25 +02:00
|
|
|
log = net["r{}".format(i)].getStdErr("bgpd")
|
2017-04-08 12:49:43 +02:00
|
|
|
if log:
|
2024-05-27 08:50:25 +02:00
|
|
|
error_logs += "r{} BGPd StdErr Output:\n".format(i)
|
2017-04-08 12:49:43 +02:00
|
|
|
error_logs += log
|
2024-05-27 08:50:25 +02:00
|
|
|
if net["r{}".format(i)].daemon_available("ldpd"):
|
|
|
|
log = net["r{}".format(i)].getStdErr("ldpd")
|
2017-04-08 12:49:43 +02:00
|
|
|
if log:
|
2024-05-27 08:50:25 +02:00
|
|
|
error_logs += "r{} LDPd StdErr Output:\n".format(i)
|
2017-04-08 12:49:43 +02:00
|
|
|
error_logs += log
|
2020-08-25 16:50:12 +02:00
|
|
|
|
|
|
|
log = net["r1"].getStdErr("nhrpd")
|
2020-11-27 03:20:16 +01:00
|
|
|
# NHRPD shows YANG model not embedded messages
|
|
|
|
# Ignore these
|
|
|
|
log = re.sub(r".*YANG model.*not embedded.*", "", log).rstrip()
|
2020-08-25 16:50:12 +02:00
|
|
|
if log:
|
2024-05-27 08:50:25 +02:00
|
|
|
error_logs += "r{} NHRPd StdErr Output:\n".format(i)
|
2020-08-25 16:50:12 +02:00
|
|
|
error_logs += log
|
|
|
|
|
|
|
|
log = net["r1"].getStdErr("babeld")
|
|
|
|
if log:
|
2024-05-27 08:50:25 +02:00
|
|
|
error_logs += "r{} BABELd StdErr Output:\n".format(i)
|
2020-08-25 16:50:12 +02:00
|
|
|
error_logs += log
|
|
|
|
|
|
|
|
log = net["r1"].getStdErr("pbrd")
|
|
|
|
if log:
|
2024-05-27 08:50:25 +02:00
|
|
|
error_logs += "r{} PBRd StdErr Output:\n".format(i)
|
2020-08-25 16:50:12 +02:00
|
|
|
error_logs += log
|
|
|
|
|
2024-05-27 08:50:25 +02:00
|
|
|
log = net["r{}".format(i)].getStdErr("zebra")
|
2017-04-08 12:49:43 +02:00
|
|
|
if log:
|
2024-05-27 08:50:25 +02:00
|
|
|
error_logs += "r{} Zebra StdErr Output:\n".format(i)
|
2017-04-08 12:49:43 +02:00
|
|
|
error_logs += log
|
|
|
|
|
|
|
|
if error_logs:
|
|
|
|
sys.stderr.write(
|
2024-05-27 08:50:25 +02:00
|
|
|
"Failed check for StdErr Output on daemons:\n{}\n".format(error_logs)
|
2017-04-08 12:49:43 +02:00
|
|
|
)
|
|
|
|
|
2017-04-11 06:07:02 +02:00
|
|
|
# Ignoring the issue if told to ignore (ie not yet fixed)
|
|
|
|
if error_logs != "":
|
2017-04-11 06:58:54 +02:00
|
|
|
if os.environ.get("bamboo_TOPOTESTS_ISSUE_349") == "IGNORE":
|
2017-04-11 06:07:02 +02:00
|
|
|
sys.stderr.write(
|
|
|
|
"Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/349\n"
|
|
|
|
)
|
|
|
|
pytest.skip(
|
|
|
|
"Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/349"
|
|
|
|
)
|
|
|
|
|
2017-04-08 12:49:43 +02:00
|
|
|
assert error_logs == "", "Daemons report errors to StdErr"
|
|
|
|
|
|
|
|
|
|
|
|
def test_converge_protocols():
|
|
|
|
global fatal_error
|
2021-07-29 11:38:55 +02:00
|
|
|
net = get_topogen().net
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
# Skip if previous fatal error condition is raised
|
|
|
|
if fatal_error != "":
|
|
|
|
pytest.skip(fatal_error)
|
|
|
|
|
|
|
|
thisDir = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
|
2023-03-24 22:58:01 +01:00
|
|
|
# We need loopback to have a link local so it always is the
|
|
|
|
# "selected" router for fe80::/64 when we static compare below.
|
|
|
|
print("Adding link-local to loopback for stable results")
|
|
|
|
cmd = (
|
|
|
|
"mac=`cat /sys/class/net/lo/address`; echo lo: $mac;"
|
|
|
|
" [ -z \"$mac\" ] && continue; IFS=':'; set $mac; unset IFS;"
|
|
|
|
" ip address add dev lo scope link"
|
|
|
|
" fe80::$(printf %02x $((0x$1 ^ 2)))$2:${3}ff:fe$4:$5$6/64"
|
|
|
|
)
|
|
|
|
net["r1"].cmd_raises(cmd)
|
|
|
|
|
2017-04-08 12:49:43 +02:00
|
|
|
print("\n\n** Waiting for protocols convergence")
|
|
|
|
print("******************************************\n")
|
|
|
|
|
|
|
|
# Not really implemented yet - just sleep 60 secs for now
|
2022-10-26 17:53:20 +02:00
|
|
|
sleep(5)
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2017-05-20 05:41:20 +02:00
|
|
|
# Make sure that all daemons are running
|
2019-02-11 18:54:31 +01:00
|
|
|
failures = 0
|
2017-05-20 05:41:20 +02:00
|
|
|
for i in range(1, 2):
|
2024-05-27 08:50:25 +02:00
|
|
|
fatal_error = net["r{}".format(i)].checkRouterRunning()
|
2017-05-20 05:41:20 +02:00
|
|
|
assert fatal_error == "", fatal_error
|
|
|
|
|
2019-02-11 18:54:31 +01:00
|
|
|
print("Show that v4 routes are right\n")
|
2024-05-27 08:50:25 +02:00
|
|
|
v4_routesFile = "{}/r{}/ipv4_routes.ref".format(thisDir, i)
|
2021-04-08 19:04:26 +02:00
|
|
|
expected = (
|
2024-05-27 08:50:25 +02:00
|
|
|
net["r{}".format(i)]
|
|
|
|
.cmd("sort {} 2> /dev/null".format(v4_routesFile))
|
|
|
|
.rstrip()
|
2021-04-08 19:04:26 +02:00
|
|
|
)
|
2019-02-11 18:54:31 +01:00
|
|
|
expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
|
2020-10-07 23:22:26 +02:00
|
|
|
|
2020-03-20 23:50:29 +01:00
|
|
|
actual = (
|
2024-05-27 08:50:25 +02:00
|
|
|
net["r{}".format(i)]
|
2020-03-20 23:50:29 +01:00
|
|
|
.cmd(
|
2021-10-06 19:49:52 +02:00
|
|
|
"vtysh -c \"show ip route\" | sed -e '/^Codes: /,/^\\s*$/d' | sort 2> /dev/null"
|
2020-10-07 23:22:26 +02:00
|
|
|
)
|
2020-03-20 23:50:29 +01:00
|
|
|
.rstrip()
|
2020-10-07 23:22:26 +02:00
|
|
|
)
|
2019-02-11 18:54:31 +01:00
|
|
|
# Drop time in last update
|
|
|
|
actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual)
|
|
|
|
actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
|
|
|
|
diff = topotest.get_textdiff(
|
|
|
|
actual,
|
|
|
|
expected,
|
|
|
|
title1="Actual IP Routing Table",
|
|
|
|
title2="Expected IP RoutingTable",
|
|
|
|
)
|
|
|
|
if diff:
|
2024-05-27 08:50:25 +02:00
|
|
|
sys.stderr.write("r{} failed IP Routing table check:\n{}\n".format(i, diff))
|
2019-02-11 18:54:31 +01:00
|
|
|
failures += 1
|
|
|
|
else:
|
2024-05-27 08:50:25 +02:00
|
|
|
print("r{} ok".format(i))
|
2019-02-11 18:54:31 +01:00
|
|
|
|
2024-05-27 08:50:25 +02:00
|
|
|
assert failures == 0, "IP Routing table failed for r{}\n{}".format(i, diff)
|
2019-02-11 18:54:31 +01:00
|
|
|
|
|
|
|
failures = 0
|
|
|
|
|
|
|
|
print("Show that v6 routes are right\n")
|
2024-05-27 08:50:25 +02:00
|
|
|
v6_routesFile = "{}/r{}/ipv6_routes.ref".format(thisDir, i)
|
2021-04-08 19:04:26 +02:00
|
|
|
expected = (
|
2024-05-27 08:50:25 +02:00
|
|
|
net["r{}".format(i)]
|
|
|
|
.cmd("sort {} 2> /dev/null".format(v6_routesFile))
|
|
|
|
.rstrip()
|
2021-04-08 19:04:26 +02:00
|
|
|
)
|
2019-02-11 18:54:31 +01:00
|
|
|
expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
|
2020-10-07 23:22:26 +02:00
|
|
|
|
2020-03-20 23:50:29 +01:00
|
|
|
actual = (
|
2024-05-27 08:50:25 +02:00
|
|
|
net["r{}".format(i)]
|
2020-03-20 23:50:29 +01:00
|
|
|
.cmd(
|
2021-10-06 19:49:52 +02:00
|
|
|
"vtysh -c \"show ipv6 route\" | sed -e '/^Codes: /,/^\\s*$/d' | sort 2> /dev/null"
|
2020-10-07 23:22:26 +02:00
|
|
|
)
|
2020-03-20 23:50:29 +01:00
|
|
|
.rstrip()
|
2020-10-07 23:22:26 +02:00
|
|
|
)
|
2019-02-11 18:54:31 +01:00
|
|
|
# Drop time in last update
|
|
|
|
actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual)
|
|
|
|
actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
|
|
|
|
diff = topotest.get_textdiff(
|
|
|
|
actual,
|
|
|
|
expected,
|
|
|
|
title1="Actual IPv6 Routing Table",
|
|
|
|
title2="Expected IPv6 RoutingTable",
|
|
|
|
)
|
|
|
|
if diff:
|
2024-05-27 08:50:25 +02:00
|
|
|
sys.stderr.write(
|
|
|
|
"r{} failed IPv6 Routing table check:\n{}\n".format(i, diff)
|
|
|
|
)
|
2019-02-11 18:54:31 +01:00
|
|
|
failures += 1
|
|
|
|
else:
|
2024-05-27 08:50:25 +02:00
|
|
|
print("r{} ok".format(i))
|
2019-02-11 18:54:31 +01:00
|
|
|
|
2024-05-27 08:50:25 +02:00
|
|
|
assert failures == 0, "IPv6 Routing table failed for r{}\n{}".format(i, diff)
|
2019-02-11 18:54:31 +01:00
|
|
|
|
2020-10-07 23:22:26 +02:00
|
|
|
|
2020-04-14 01:00:18 +02:00
|
|
|
def route_get_nhg_id(route_str):
|
2024-08-14 14:37:00 +02:00
|
|
|
global fatal_error
|
2020-04-14 01:00:18 +02:00
|
|
|
|
2024-08-14 14:37:00 +02:00
|
|
|
def get_func(route_str):
|
|
|
|
net = get_topogen().net
|
|
|
|
output = net["r1"].cmd(
|
|
|
|
'vtysh -c "show ip route {} nexthop-group"'.format(route_str)
|
|
|
|
)
|
|
|
|
match = re.search(r"Nexthop Group ID: (\d+)", output)
|
|
|
|
if match is not None:
|
|
|
|
nhg_id = int(match.group(1))
|
|
|
|
return nhg_id
|
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
|
|
|
test_func = functools.partial(get_func, route_str)
|
|
|
|
_, nhg_id = topotest.run_and_expect_type(test_func, int, count=30, wait=1)
|
2025-04-01 08:36:15 +02:00
|
|
|
if nhg_id is None:
|
2024-08-14 14:37:00 +02:00
|
|
|
fatal_error = "Nexthop Group ID not found for route {}".format(route_str)
|
2025-04-01 08:36:15 +02:00
|
|
|
assert nhg_id is not None, fatal_error
|
2024-08-14 14:37:00 +02:00
|
|
|
else:
|
|
|
|
return nhg_id
|
2020-04-14 01:00:18 +02:00
|
|
|
|
2020-10-07 23:22:26 +02:00
|
|
|
|
2020-08-03 20:43:01 +02:00
|
|
|
def verify_nexthop_group(nhg_id, recursive=False, ecmp=0):
|
2021-07-29 11:38:55 +02:00
|
|
|
net = get_topogen().net
|
2021-10-21 16:21:40 +02:00
|
|
|
count = 0
|
|
|
|
valid = None
|
|
|
|
ecmpcount = None
|
|
|
|
depends = None
|
|
|
|
resolved_id = None
|
|
|
|
installed = None
|
|
|
|
found = False
|
|
|
|
|
|
|
|
while not found and count < 10:
|
|
|
|
count += 1
|
|
|
|
# Verify NHG is valid/installed
|
2024-05-27 08:50:25 +02:00
|
|
|
output = net["r1"].cmd('vtysh -c "show nexthop-group rib {}"'.format(nhg_id))
|
2021-10-21 16:21:40 +02:00
|
|
|
valid = re.search(r"Valid", output)
|
|
|
|
if valid is None:
|
|
|
|
found = False
|
|
|
|
sleep(1)
|
|
|
|
continue
|
|
|
|
|
|
|
|
if ecmp or recursive:
|
|
|
|
ecmpcount = re.search(r"Depends:.*\n", output)
|
|
|
|
if ecmpcount is None:
|
|
|
|
found = False
|
|
|
|
sleep(1)
|
|
|
|
continue
|
|
|
|
|
|
|
|
# list of IDs in group
|
|
|
|
depends = re.findall(r"\((\d+)\)", ecmpcount.group(0))
|
|
|
|
|
|
|
|
if ecmp:
|
|
|
|
if len(depends) != ecmp:
|
|
|
|
found = False
|
|
|
|
sleep(1)
|
|
|
|
continue
|
|
|
|
else:
|
|
|
|
# If recursive, we need to look at its resolved group
|
|
|
|
if len(depends) != 1:
|
|
|
|
found = False
|
|
|
|
sleep(1)
|
|
|
|
continue
|
|
|
|
|
|
|
|
resolved_id = int(depends[0])
|
|
|
|
verify_nexthop_group(resolved_id, False)
|
|
|
|
else:
|
|
|
|
installed = re.search(r"Installed", output)
|
|
|
|
if installed is None:
|
|
|
|
found = False
|
|
|
|
sleep(1)
|
|
|
|
continue
|
|
|
|
found = True
|
|
|
|
|
2024-05-27 08:50:25 +02:00
|
|
|
assert valid is not None, "Nexthop Group ID={} not marked Valid".format(nhg_id)
|
2020-08-03 20:43:01 +02:00
|
|
|
if ecmp or recursive:
|
2024-05-27 08:50:25 +02:00
|
|
|
assert ecmpcount is not None, "Nexthop Group ID={} has no depends".format(
|
|
|
|
nhg_id
|
|
|
|
)
|
2020-08-03 20:43:01 +02:00
|
|
|
if ecmp:
|
2024-05-27 08:50:25 +02:00
|
|
|
assert (
|
|
|
|
len(depends) == ecmp
|
|
|
|
), "Nexthop Group ID={} doesn't match ecmp size".format(nhg_id)
|
2020-08-03 20:43:01 +02:00
|
|
|
else:
|
2024-05-27 08:50:25 +02:00
|
|
|
assert (
|
|
|
|
len(depends) == 1
|
|
|
|
), "Nexthop Group ID={} should only have one recursive depend".format(
|
|
|
|
nhg_id
|
2020-10-07 23:22:26 +02:00
|
|
|
)
|
2020-04-14 01:00:18 +02:00
|
|
|
else:
|
2024-05-27 08:50:25 +02:00
|
|
|
assert installed is not None, "Nexthop Group ID={} not marked Installed".format(
|
|
|
|
nhg_id
|
2021-10-21 16:21:40 +02:00
|
|
|
)
|
2020-04-14 01:00:18 +02:00
|
|
|
|
2020-10-07 23:22:26 +02:00
|
|
|
|
2020-08-03 20:43:01 +02:00
|
|
|
def verify_route_nexthop_group(route_str, recursive=False, ecmp=0):
|
2024-08-14 14:37:00 +02:00
|
|
|
global fatal_error
|
|
|
|
|
2020-04-14 01:00:18 +02:00
|
|
|
# Verify route and that zebra created NHGs for and they are valid/installed
|
2024-08-14 14:37:00 +02:00
|
|
|
|
2020-04-14 01:00:18 +02:00
|
|
|
nhg_id = route_get_nhg_id(route_str)
|
2024-08-14 14:37:00 +02:00
|
|
|
|
2020-08-03 20:43:01 +02:00
|
|
|
verify_nexthop_group(nhg_id, recursive, ecmp)
|
2020-04-14 01:00:18 +02:00
|
|
|
|
2020-10-07 23:22:26 +02:00
|
|
|
|
2019-12-31 23:10:58 +01:00
|
|
|
def test_nexthop_groups():
|
|
|
|
global fatal_error
|
2021-07-29 11:38:55 +02:00
|
|
|
net = get_topogen().net
|
2019-12-31 23:10:58 +01:00
|
|
|
|
|
|
|
# Skip if previous fatal error condition is raised
|
|
|
|
if fatal_error != "":
|
|
|
|
pytest.skip(fatal_error)
|
|
|
|
|
|
|
|
print("\n\n** Verifying Nexthop Groups")
|
|
|
|
print("******************************************\n")
|
|
|
|
|
2020-04-14 01:00:18 +02:00
|
|
|
### Nexthop Group Tests
|
|
|
|
|
|
|
|
## Basic test
|
|
|
|
|
2019-12-31 23:10:58 +01:00
|
|
|
# Create a lib nexthop-group
|
2020-04-14 01:00:18 +02:00
|
|
|
net["r1"].cmd(
|
|
|
|
'vtysh -c "c t" -c "nexthop-group basic" -c "nexthop 1.1.1.1" -c "nexthop 1.1.1.2"'
|
|
|
|
)
|
2019-12-31 23:10:58 +01:00
|
|
|
|
|
|
|
# Create with sharpd using nexthop-group
|
2020-04-14 01:00:18 +02:00
|
|
|
net["r1"].cmd('vtysh -c "sharp install routes 2.2.2.1 nexthop-group basic 1"')
|
|
|
|
verify_route_nexthop_group("2.2.2.1/32")
|
2019-12-31 23:10:58 +01:00
|
|
|
|
2020-04-14 01:00:18 +02:00
|
|
|
## Connected
|
2019-12-31 23:10:58 +01:00
|
|
|
|
2020-04-14 01:00:18 +02:00
|
|
|
net["r1"].cmd(
|
|
|
|
'vtysh -c "c t" -c "nexthop-group connected" -c "nexthop r1-eth1" -c "nexthop r1-eth2"'
|
|
|
|
)
|
2019-12-31 23:10:58 +01:00
|
|
|
|
2020-04-14 01:00:18 +02:00
|
|
|
net["r1"].cmd('vtysh -c "sharp install routes 2.2.2.2 nexthop-group connected 1"')
|
|
|
|
verify_route_nexthop_group("2.2.2.2/32")
|
|
|
|
|
|
|
|
## Recursive
|
|
|
|
|
|
|
|
net["r1"].cmd(
|
|
|
|
'vtysh -c "c t" -c "nexthop-group basic-recursive" -c "nexthop 2.2.2.1"'
|
|
|
|
)
|
|
|
|
|
|
|
|
net["r1"].cmd(
|
|
|
|
'vtysh -c "sharp install routes 3.3.3.1 nexthop-group basic-recursive 1"'
|
|
|
|
)
|
|
|
|
|
|
|
|
verify_route_nexthop_group("3.3.3.1/32", True)
|
|
|
|
|
|
|
|
## Duplicate
|
|
|
|
|
|
|
|
net["r1"].cmd(
|
|
|
|
'vtysh -c "c t" -c "nexthop-group duplicate" -c "nexthop 2.2.2.1" -c "nexthop 1.1.1.1"'
|
|
|
|
)
|
|
|
|
|
|
|
|
net["r1"].cmd('vtysh -c "sharp install routes 3.3.3.2 nexthop-group duplicate 1"')
|
|
|
|
|
|
|
|
verify_route_nexthop_group("3.3.3.2/32")
|
|
|
|
|
|
|
|
## Two 4-Way ECMP
|
|
|
|
|
|
|
|
net["r1"].cmd(
|
|
|
|
'vtysh -c "c t" -c "nexthop-group fourA" -c "nexthop 1.1.1.1" -c "nexthop 1.1.1.2" \
|
|
|
|
-c "nexthop 1.1.1.3" -c "nexthop 1.1.1.4"'
|
|
|
|
)
|
|
|
|
|
|
|
|
net["r1"].cmd('vtysh -c "sharp install routes 4.4.4.1 nexthop-group fourA 1"')
|
|
|
|
|
|
|
|
verify_route_nexthop_group("4.4.4.1/32")
|
|
|
|
|
|
|
|
net["r1"].cmd(
|
|
|
|
'vtysh -c "c t" -c "nexthop-group fourB" -c "nexthop 1.1.1.5" -c "nexthop 1.1.1.6" \
|
|
|
|
-c "nexthop 1.1.1.7" -c "nexthop 1.1.1.8"'
|
|
|
|
)
|
|
|
|
|
|
|
|
net["r1"].cmd('vtysh -c "sharp install routes 4.4.4.2 nexthop-group fourB 1"')
|
|
|
|
|
|
|
|
verify_route_nexthop_group("4.4.4.2/32")
|
|
|
|
|
|
|
|
## Recursive to 8-Way ECMP
|
|
|
|
|
|
|
|
net["r1"].cmd(
|
|
|
|
'vtysh -c "c t" -c "nexthop-group eight-recursive" -c "nexthop 4.4.4.1" -c "nexthop 4.4.4.2"'
|
|
|
|
)
|
|
|
|
|
|
|
|
net["r1"].cmd(
|
|
|
|
'vtysh -c "sharp install routes 5.5.5.1 nexthop-group eight-recursive 1"'
|
|
|
|
)
|
|
|
|
|
|
|
|
verify_route_nexthop_group("5.5.5.1/32")
|
|
|
|
|
2021-01-29 21:58:34 +01:00
|
|
|
## 4-way ECMP Routes Pointing to Each Other
|
|
|
|
|
|
|
|
# This is to check for a bug with NH resolution where
|
|
|
|
# routes would infintely resolve to each other blowing
|
|
|
|
# up the resolved-> nexthop pointer.
|
|
|
|
|
|
|
|
net["r1"].cmd(
|
|
|
|
'vtysh -c "c t" -c "nexthop-group infinite-recursive" -c "nexthop 6.6.6.1" -c "nexthop 6.6.6.2" \
|
|
|
|
-c "nexthop 6.6.6.3" -c "nexthop 6.6.6.4"'
|
|
|
|
)
|
|
|
|
|
|
|
|
# static route nexthops can recurse to
|
|
|
|
|
|
|
|
net["r1"].cmd('vtysh -c "c t" -c "ip route 6.6.6.0/24 1.1.1.1"')
|
|
|
|
|
|
|
|
# Make routes that point to themselves in ecmp
|
|
|
|
|
|
|
|
net["r1"].cmd(
|
|
|
|
'vtysh -c "sharp install routes 6.6.6.4 nexthop-group infinite-recursive 1"'
|
|
|
|
)
|
2021-11-01 20:08:50 +01:00
|
|
|
sleep(5)
|
2021-01-29 21:58:34 +01:00
|
|
|
|
|
|
|
net["r1"].cmd(
|
|
|
|
'vtysh -c "sharp install routes 6.6.6.3 nexthop-group infinite-recursive 1"'
|
|
|
|
)
|
2021-11-01 20:08:50 +01:00
|
|
|
sleep(5)
|
2021-01-29 21:58:34 +01:00
|
|
|
|
|
|
|
net["r1"].cmd(
|
|
|
|
'vtysh -c "sharp install routes 6.6.6.2 nexthop-group infinite-recursive 1"'
|
|
|
|
)
|
2021-11-01 20:08:50 +01:00
|
|
|
sleep(5)
|
2021-01-29 21:58:34 +01:00
|
|
|
|
|
|
|
net["r1"].cmd(
|
|
|
|
'vtysh -c "sharp install routes 6.6.6.1 nexthop-group infinite-recursive 1"'
|
|
|
|
)
|
|
|
|
|
|
|
|
# Get routes and test if has too many (duplicate) nexthops
|
2021-10-21 16:21:40 +02:00
|
|
|
count = 0
|
|
|
|
dups = []
|
2021-01-29 21:58:34 +01:00
|
|
|
nhg_id = route_get_nhg_id("6.6.6.1/32")
|
zebra: Allow longer prefix matches for nexthops
Zebra currently does a shortest prefix match for
resolving nexthops for a prefix. This is typically
an ok thing to do but fails in several specific scenarios.
If a nexthop matches to a route that is not usable, nexthop
resolution just gives up and refuses to use that particular
route. For example if zebra currently has a covering prefix
say a 10.0.0.0/8. And about the same time it receives a
10.1.0.0/16 ( a more specific than the /8 ) and another
route A, who's nexthop is 10.1.1.1. Imagine the 10.1.0.0/16
is processed enough to know we want to install it and the
prefix is sent to the dataplane for installation( it is queued )
and then route A is processed, nexthop resolution will fail
and the route A will be left in limbo as uninstallable.
Let's modify the nexthop resolution code in zebra such that
if a nexthop's most specific match is unusable, continue looking
up the table till we get to the 0.0.0.0/0 route( if it's even
installed ). If we find a usable route for the nexthop accept
it and use it.
The bgp_default_originate topology test is frequently failing
with this exact problem:
B>* 0.0.0.0/0 [200/0] via 192.168.1.1, r2-r1-eth0, weight 1, 00:00:21
B 1.0.1.17/32 [200/0] via 192.168.0.1 inactive, weight 1, 00:00:21
B>* 1.0.2.17/32 [200/0] via 192.168.1.1, r2-r1-eth0, weight 1, 00:00:21
C>* 1.0.3.17/32 is directly connected, lo, 00:02:00
B>* 1.0.5.17/32 [20/0] via 192.168.2.2, r2-r3-eth1, weight 1, 00:00:32
B>* 192.168.0.0/24 [200/0] via 192.168.1.1, r2-r1-eth0, weight 1, 00:00:21
B 192.168.1.0/24 [200/0] via 192.168.1.1 inactive, weight 1, 00:00:21
C>* 192.168.1.0/24 is directly connected, r2-r1-eth0, 00:02:00
C>* 192.168.2.0/24 is directly connected, r2-r3-eth1, 00:02:00
B>* 192.168.3.0/24 [20/0] via 192.168.2.2, r2-r3-eth1, weight 1, 00:00:32
B 198.51.1.1/32 [200/0] via 192.168.0.1 inactive, weight 1, 00:00:21
B>* 198.51.1.2/32 [20/0] via 192.168.2.2, r2-r3-eth1, weight 1, 00:00:32
Notice that the 1.0.1.17/32 route is inactive but the nexthop
192.168.0.1 is covered by both the 192.168.0.0/24 prefix( shortest match )
*and* the 0.0.0.0/0 route ( longest match ). When looking at the logs
the 1.0.1.17/32 route was not being installed because the matching
route was not in a usable state, which is because the 192.168.0.0/24
route was in the process of being installed.
Signed-off-by: Donald Sharp <sharpd@nvidia.com>
2023-10-19 22:38:12 +02:00
|
|
|
while (len(dups) != 4) and count < 10:
|
2024-05-27 08:50:25 +02:00
|
|
|
output = net["r1"].cmd('vtysh -c "show nexthop-group rib {}"'.format(nhg_id))
|
2021-01-29 21:58:34 +01:00
|
|
|
|
2021-10-21 16:21:40 +02:00
|
|
|
dups = re.findall(r"(via 1\.1\.1\.1)", output)
|
zebra: Allow longer prefix matches for nexthops
Zebra currently does a shortest prefix match for
resolving nexthops for a prefix. This is typically
an ok thing to do but fails in several specific scenarios.
If a nexthop matches to a route that is not usable, nexthop
resolution just gives up and refuses to use that particular
route. For example if zebra currently has a covering prefix
say a 10.0.0.0/8. And about the same time it receives a
10.1.0.0/16 ( a more specific than the /8 ) and another
route A, who's nexthop is 10.1.1.1. Imagine the 10.1.0.0/16
is processed enough to know we want to install it and the
prefix is sent to the dataplane for installation( it is queued )
and then route A is processed, nexthop resolution will fail
and the route A will be left in limbo as uninstallable.
Let's modify the nexthop resolution code in zebra such that
if a nexthop's most specific match is unusable, continue looking
up the table till we get to the 0.0.0.0/0 route( if it's even
installed ). If we find a usable route for the nexthop accept
it and use it.
The bgp_default_originate topology test is frequently failing
with this exact problem:
B>* 0.0.0.0/0 [200/0] via 192.168.1.1, r2-r1-eth0, weight 1, 00:00:21
B 1.0.1.17/32 [200/0] via 192.168.0.1 inactive, weight 1, 00:00:21
B>* 1.0.2.17/32 [200/0] via 192.168.1.1, r2-r1-eth0, weight 1, 00:00:21
C>* 1.0.3.17/32 is directly connected, lo, 00:02:00
B>* 1.0.5.17/32 [20/0] via 192.168.2.2, r2-r3-eth1, weight 1, 00:00:32
B>* 192.168.0.0/24 [200/0] via 192.168.1.1, r2-r1-eth0, weight 1, 00:00:21
B 192.168.1.0/24 [200/0] via 192.168.1.1 inactive, weight 1, 00:00:21
C>* 192.168.1.0/24 is directly connected, r2-r1-eth0, 00:02:00
C>* 192.168.2.0/24 is directly connected, r2-r3-eth1, 00:02:00
B>* 192.168.3.0/24 [20/0] via 192.168.2.2, r2-r3-eth1, weight 1, 00:00:32
B 198.51.1.1/32 [200/0] via 192.168.0.1 inactive, weight 1, 00:00:21
B>* 198.51.1.2/32 [20/0] via 192.168.2.2, r2-r3-eth1, weight 1, 00:00:32
Notice that the 1.0.1.17/32 route is inactive but the nexthop
192.168.0.1 is covered by both the 192.168.0.0/24 prefix( shortest match )
*and* the 0.0.0.0/0 route ( longest match ). When looking at the logs
the 1.0.1.17/32 route was not being installed because the matching
route was not in a usable state, which is because the 192.168.0.0/24
route was in the process of being installed.
Signed-off-by: Donald Sharp <sharpd@nvidia.com>
2023-10-19 22:38:12 +02:00
|
|
|
if len(dups) != 4:
|
2021-10-21 16:21:40 +02:00
|
|
|
count += 1
|
|
|
|
sleep(1)
|
2021-01-29 21:58:34 +01:00
|
|
|
|
|
|
|
# Should find 3, itself is inactive
|
2024-05-27 08:50:25 +02:00
|
|
|
assert (
|
|
|
|
len(dups) == 4
|
|
|
|
), "Route 6.6.6.1/32 with Nexthop Group ID={} has wrong number of resolved nexthops".format(
|
|
|
|
nhg_id
|
2021-01-29 21:58:34 +01:00
|
|
|
)
|
|
|
|
|
zebra: Fix reinstalling nexthops in NHGs upon interface flaps
Trigger:
Imagine a route utilizing an NHG with six nexthops (Intf swp1-swp6).
If interfaces swp1-swp4 flaps, the NHG remains the same but now only
references two nexthops (swp5-6) instead of all six. This behavior
occurs due to how NHGs with recursive nexthops are managed within Zebra.
In the scenario below, NHG 370 has all six nexthops installed in the
kernel. However, Zebra maintains a list of recursive NHGs that NHG 370
references i.e., Depends: (371), (372), (373) which are not directly
installed in the kernel.
- When an interface comes up, its nexthop and corresponding dependents
are installed.
- These dependents (counterparts to 371-373) are non-recursive and
are installed as well.
- However, when attempting to install the recursive ones in
zebra_nhg_install_kernel(), they resolve to the already installed
counterparts, resulting in a NO-OP.
Fixing this by iterating all dependents of the recursively resolved
NHGs and reinstalling them.
Trigger: Flap swp1 to swp4
Before Fix:
root@leaf-11:mgmt:/var/home/cumulus# ip route show | grep 6.0.0.5
6.0.0.5 nhid 370 proto bgp metric 20
ip -d next show
id 337 via 2000:1:0:1:0:f:0:9 dev swp6 scope link proto zebra
id 339 via 2000:1:0:1:0:e:0:9 dev swp5 scope link proto zebra
id 341 via 2000:1:0:1:0:8:0:8 dev swp4 scope link proto zebra
id 343 via 2000:1:0:1:0:7:0:8 dev swp3 scope link proto zebra
id 346 via 2000:1:0:1:0:1:0:7 dev swp2 scope link proto zebra
id 348 via 2000:1:0:1::7 dev swp1 scope link proto zebra
id 370 group 346/348/341/343/337/339 scope global proto zebra
After Trigger:
root@leaf-11:mgmt:/var/home/cumulus# ip route show | grep 6.0.0.5
6.0.0.5 nhid 370 proto bgp metric 20
root@leaf-11:mgmt:/var/home/cumulus# ip -d next show
id 337 via 2000:1:0:1:0:f:0:9 dev swp6 scope link proto zebra
id 339 via 2000:1:0:1:0:e:0:9 dev swp5 scope link proto zebra
id 370 group 337/339 scope global proto zebra
After Fix:
root@leaf-11:mgmt:/var/home/cumulus# ip route show | grep 6.0.0.5
6.0.0.5 nhid 432 proto bgp metric 20
ip -d next show
id 432 group 395/397/400/402/405/407 scope global proto zebra
After Trigger
root@leaf-11:mgmt:/var/home/cumulus# ip route show | grep 6.0.0.5
6.0.0.5 nhid 432 proto bgp metric 20
root@leaf-11:mgmt:/var/home/cumulus# ip -d next show
id 432 group 395/397/400/402/405/407 scope global proto zebra
Ticket :#
Signed-off-by: Rajasekar Raja <rajasekarr@nvidia.com>
Signed-off-by: Donald Sharp <sharpd@nvidia.com>
2025-03-11 20:15:32 +01:00
|
|
|
## Validate NHG's installed in kernel has same nexthops with Interface flaps
|
|
|
|
pre_out = net["r1"].cmd('ip route show | grep "5.5.5.1"')
|
|
|
|
pre_nhg = re.search(r"nhid\s+(\d+)", pre_out)
|
|
|
|
pre_nh_show = net["r1"].cmd("ip next show id {}".format(pre_nhg.group(1)))
|
|
|
|
pre_total_nhs = len((re.search(r"group ([\d/]+)", pre_nh_show)).group(1).split("/"))
|
|
|
|
|
|
|
|
net["r1"].cmd(
|
|
|
|
"ip link set r1-eth1 down;ip link set r1-eth2 down;ip link set r1-eth3 down;ip link set r1-eth4 down"
|
|
|
|
)
|
|
|
|
sleep(1)
|
|
|
|
net["r1"].cmd(
|
|
|
|
"ip link set r1-eth1 up;ip link set r1-eth2 up;ip link set r1-eth3 up;ip link set r1-eth4 up"
|
|
|
|
)
|
|
|
|
sleep(5)
|
|
|
|
|
|
|
|
post_out = net["r1"].cmd('ip route show | grep "5.5.5.1"')
|
|
|
|
post_nhg = re.search(r"nhid\s+(\d+)", post_out)
|
|
|
|
post_nh_show = net["r1"].cmd("ip next show id {}".format(post_nhg.group(1)))
|
|
|
|
post_total_nhs = len(
|
|
|
|
(re.search(r"group ([\d/]+)", post_nh_show)).group(1).split("/")
|
|
|
|
)
|
|
|
|
|
|
|
|
assert (
|
|
|
|
post_total_nhs == pre_total_nhs
|
|
|
|
), "Expected same nexthops(pre-{}: post-{}) in NHG (pre-{}:post-{}) after few Interface flaps".format(
|
|
|
|
pre_total_nhs, post_total_nhs, pre_nhg.group(1), post_nhg.group(1)
|
|
|
|
)
|
|
|
|
|
2020-04-14 01:00:18 +02:00
|
|
|
## Remove all NHG routes
|
|
|
|
|
|
|
|
net["r1"].cmd('vtysh -c "sharp remove routes 2.2.2.1 1"')
|
|
|
|
net["r1"].cmd('vtysh -c "sharp remove routes 2.2.2.2 1"')
|
|
|
|
net["r1"].cmd('vtysh -c "sharp remove routes 3.3.3.1 1"')
|
|
|
|
net["r1"].cmd('vtysh -c "sharp remove routes 3.3.3.2 1"')
|
|
|
|
net["r1"].cmd('vtysh -c "sharp remove routes 4.4.4.1 1"')
|
|
|
|
net["r1"].cmd('vtysh -c "sharp remove routes 4.4.4.2 1"')
|
|
|
|
net["r1"].cmd('vtysh -c "sharp remove routes 5.5.5.1 1"')
|
2021-01-29 21:58:34 +01:00
|
|
|
net["r1"].cmd('vtysh -c "sharp remove routes 6.6.6.1 4"')
|
|
|
|
net["r1"].cmd('vtysh -c "c t" -c "no ip route 6.6.6.0/24 1.1.1.1"')
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2020-10-07 23:22:26 +02:00
|
|
|
|
2017-04-08 12:49:43 +02:00
|
|
|
def test_rip_status():
|
|
|
|
global fatal_error
|
2021-07-29 11:38:55 +02:00
|
|
|
net = get_topogen().net
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
# Skip if previous fatal error condition is raised
|
|
|
|
if fatal_error != "":
|
|
|
|
pytest.skip(fatal_error)
|
|
|
|
|
|
|
|
thisDir = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
|
2017-04-26 18:18:58 +02:00
|
|
|
print("\n\n** Verifying RIP status")
|
2017-04-08 12:49:43 +02:00
|
|
|
print("******************************************\n")
|
|
|
|
failures = 0
|
|
|
|
for i in range(1, 2):
|
2024-05-27 08:50:25 +02:00
|
|
|
refTableFile = "{}/r{}/rip_status.ref".format(thisDir, i)
|
2017-04-08 12:49:43 +02:00
|
|
|
if os.path.isfile(refTableFile):
|
|
|
|
# Read expected result from file
|
2025-04-01 08:36:15 +02:00
|
|
|
with open(refTableFile) as file:
|
|
|
|
expected = file.read().rstrip()
|
2017-04-08 12:49:43 +02:00
|
|
|
# Fix newlines (make them all the same)
|
|
|
|
expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
|
|
|
|
|
|
|
|
# Actual output from router
|
|
|
|
actual = (
|
2024-05-27 08:50:25 +02:00
|
|
|
net["r{}".format(i)]
|
2017-04-08 12:49:43 +02:00
|
|
|
.cmd('vtysh -c "show ip rip status" 2> /dev/null')
|
|
|
|
.rstrip()
|
2020-10-07 23:22:26 +02:00
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
# Drop time in next due
|
|
|
|
actual = re.sub(r"in [0-9]+ seconds", "in XX seconds", actual)
|
|
|
|
# Drop time in last update
|
|
|
|
actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual)
|
|
|
|
# Fix newlines (make them all the same)
|
|
|
|
actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
|
|
|
|
|
|
|
|
# Generate Diff
|
2017-05-31 04:39:21 +02:00
|
|
|
diff = topotest.get_textdiff(
|
|
|
|
actual,
|
|
|
|
expected,
|
|
|
|
title1="actual IP RIP status",
|
|
|
|
title2="expected IP RIP status",
|
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
# Empty string if it matches, otherwise diff contains unified diff
|
|
|
|
if diff:
|
2024-05-27 08:50:25 +02:00
|
|
|
sys.stderr.write(
|
|
|
|
"r{} failed IP RIP status check:\n{}\n".format(i, diff)
|
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
failures += 1
|
|
|
|
else:
|
2024-05-27 08:50:25 +02:00
|
|
|
print("r{} ok".format(i))
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2024-05-27 08:50:25 +02:00
|
|
|
assert failures == 0, "IP RIP status failed for router r{}:\n{}".format(
|
|
|
|
i, diff
|
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2017-05-20 05:41:20 +02:00
|
|
|
# Make sure that all daemons are running
|
|
|
|
for i in range(1, 2):
|
2024-05-27 08:50:25 +02:00
|
|
|
fatal_error = net["r{}".format(i)].checkRouterRunning()
|
2017-05-20 05:41:20 +02:00
|
|
|
assert fatal_error == "", fatal_error
|
|
|
|
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
def test_ripng_status():
|
|
|
|
global fatal_error
|
2021-07-29 11:38:55 +02:00
|
|
|
net = get_topogen().net
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
# Skip if previous fatal error condition is raised
|
|
|
|
if fatal_error != "":
|
|
|
|
pytest.skip(fatal_error)
|
|
|
|
|
|
|
|
thisDir = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
|
2017-04-26 18:18:58 +02:00
|
|
|
print("\n\n** Verifying RIPng status")
|
2017-04-08 12:49:43 +02:00
|
|
|
print("******************************************\n")
|
|
|
|
failures = 0
|
|
|
|
for i in range(1, 2):
|
2024-05-27 08:50:25 +02:00
|
|
|
refTableFile = "{}/r{}/ripng_status.ref".format(thisDir, i)
|
2017-04-08 12:49:43 +02:00
|
|
|
if os.path.isfile(refTableFile):
|
|
|
|
# Read expected result from file
|
2025-04-01 08:36:15 +02:00
|
|
|
with open(refTableFile) as file:
|
|
|
|
expected = file.read().rstrip()
|
2017-04-08 12:49:43 +02:00
|
|
|
# Fix newlines (make them all the same)
|
|
|
|
expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
|
|
|
|
|
|
|
|
# Actual output from router
|
|
|
|
actual = (
|
2024-05-27 08:50:25 +02:00
|
|
|
net["r{}".format(i)]
|
2017-04-08 12:49:43 +02:00
|
|
|
.cmd('vtysh -c "show ipv6 ripng status" 2> /dev/null')
|
|
|
|
.rstrip()
|
2020-10-07 23:22:26 +02:00
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
# Mask out Link-Local mac address portion. They are random...
|
|
|
|
actual = re.sub(r" fe80::[0-9a-f:]+", " fe80::XXXX:XXXX:XXXX:XXXX", actual)
|
|
|
|
# Drop time in next due
|
|
|
|
actual = re.sub(r"in [0-9]+ seconds", "in XX seconds", actual)
|
|
|
|
# Drop time in last update
|
|
|
|
actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual)
|
|
|
|
# Fix newlines (make them all the same)
|
|
|
|
actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
|
|
|
|
|
|
|
|
# Generate Diff
|
2017-05-31 04:39:21 +02:00
|
|
|
diff = topotest.get_textdiff(
|
|
|
|
actual,
|
|
|
|
expected,
|
|
|
|
title1="actual IPv6 RIPng status",
|
|
|
|
title2="expected IPv6 RIPng status",
|
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
# Empty string if it matches, otherwise diff contains unified diff
|
|
|
|
if diff:
|
|
|
|
sys.stderr.write(
|
2024-05-27 08:50:25 +02:00
|
|
|
"r{} failed IPv6 RIPng status check:\n{}\n".format(i, diff)
|
2020-10-07 23:22:26 +02:00
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
failures += 1
|
|
|
|
else:
|
2024-05-27 08:50:25 +02:00
|
|
|
print("r{} ok".format(i))
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2024-05-27 08:50:25 +02:00
|
|
|
assert failures == 0, "IPv6 RIPng status failed for router r{}:\n{}".format(
|
|
|
|
i, diff
|
2017-04-08 12:49:43 +02:00
|
|
|
)
|
|
|
|
|
2017-05-20 05:41:20 +02:00
|
|
|
# Make sure that all daemons are running
|
|
|
|
for i in range(1, 2):
|
2024-05-27 08:50:25 +02:00
|
|
|
fatal_error = net["r{}".format(i)].checkRouterRunning()
|
2017-05-20 05:41:20 +02:00
|
|
|
assert fatal_error == "", fatal_error
|
|
|
|
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
def test_ospfv2_interfaces():
|
|
|
|
global fatal_error
|
2021-07-29 11:38:55 +02:00
|
|
|
net = get_topogen().net
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
# Skip if previous fatal error condition is raised
|
|
|
|
if fatal_error != "":
|
|
|
|
pytest.skip(fatal_error)
|
|
|
|
|
|
|
|
thisDir = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
|
2017-04-26 18:18:58 +02:00
|
|
|
print("\n\n** Verifying OSPFv2 interfaces")
|
2017-04-08 12:49:43 +02:00
|
|
|
print("******************************************\n")
|
|
|
|
failures = 0
|
|
|
|
for i in range(1, 2):
|
2024-05-27 08:50:25 +02:00
|
|
|
refTableFile = "{}/r{}/show_ip_ospf_interface.ref".format(thisDir, i)
|
2017-04-08 12:49:43 +02:00
|
|
|
if os.path.isfile(refTableFile):
|
|
|
|
# Read expected result from file
|
2025-04-01 08:36:15 +02:00
|
|
|
with open(refTableFile) as file:
|
|
|
|
expected = file.read().rstrip()
|
2017-04-08 12:49:43 +02:00
|
|
|
# Fix newlines (make them all the same)
|
|
|
|
expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
|
|
|
|
|
|
|
|
# Actual output from router
|
|
|
|
actual = (
|
2024-05-27 08:50:25 +02:00
|
|
|
net["r{}".format(i)]
|
2017-04-08 12:49:43 +02:00
|
|
|
.cmd('vtysh -c "show ip ospf interface" 2> /dev/null')
|
|
|
|
.rstrip()
|
2020-10-07 23:22:26 +02:00
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
# Mask out Bandwidth portion. They may change..
|
|
|
|
actual = re.sub(r"BW [0-9]+ Mbit", "BW XX Mbit", actual)
|
2021-08-27 20:28:29 +02:00
|
|
|
actual = re.sub(r"ifindex [0-9]+", "ifindex X", actual)
|
2019-01-25 03:46:35 +01:00
|
|
|
|
2017-04-08 12:49:43 +02:00
|
|
|
# Drop time in next due
|
|
|
|
actual = re.sub(r"Hello due in [0-9\.]+s", "Hello due in XX.XXXs", actual)
|
2020-07-07 13:36:54 +02:00
|
|
|
actual = re.sub(
|
|
|
|
r"Hello due in [0-9\.]+ usecs", "Hello due in XX.XXXs", actual
|
|
|
|
)
|
2017-07-07 03:12:05 +02:00
|
|
|
# Fix 'MTU mismatch detection: enabled' vs 'MTU mismatch detection:enabled' - accept both
|
|
|
|
actual = re.sub(
|
|
|
|
r"MTU mismatch detection:([a-z]+.*)",
|
|
|
|
r"MTU mismatch detection: \1",
|
|
|
|
actual,
|
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
# Fix newlines (make them all the same)
|
|
|
|
actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
|
|
|
|
|
|
|
|
# Generate Diff
|
2017-05-31 04:39:21 +02:00
|
|
|
diff = topotest.get_textdiff(
|
|
|
|
actual,
|
|
|
|
expected,
|
|
|
|
title1="actual SHOW IP OSPF INTERFACE",
|
|
|
|
title2="expected SHOW IP OSPF INTERFACE",
|
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
# Empty string if it matches, otherwise diff contains unified diff
|
|
|
|
if diff:
|
|
|
|
sys.stderr.write(
|
2024-05-27 08:50:25 +02:00
|
|
|
"r{} failed SHOW IP OSPF INTERFACE check:\n{}\n".format(i, diff)
|
2020-10-07 23:22:26 +02:00
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
failures += 1
|
|
|
|
else:
|
2024-05-27 08:50:25 +02:00
|
|
|
print("r{} ok".format(i))
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2017-04-11 06:07:02 +02:00
|
|
|
# Ignoring the issue if told to ignore (ie not yet fixed)
|
|
|
|
if failures != 0:
|
2017-04-11 06:58:54 +02:00
|
|
|
if os.environ.get("bamboo_TOPOTESTS_ISSUE_348") == "IGNORE":
|
2017-04-11 06:07:02 +02:00
|
|
|
sys.stderr.write(
|
|
|
|
"Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/348\n"
|
|
|
|
)
|
|
|
|
pytest.skip(
|
|
|
|
"Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/348"
|
|
|
|
)
|
2020-10-07 23:22:26 +02:00
|
|
|
|
2017-04-08 12:49:43 +02:00
|
|
|
assert (
|
|
|
|
failures == 0
|
2024-05-27 08:50:25 +02:00
|
|
|
), "SHOW IP OSPF INTERFACE failed for router r{}:\n{}".format(i, diff)
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2017-05-20 05:41:20 +02:00
|
|
|
# Make sure that all daemons are running
|
|
|
|
for i in range(1, 2):
|
2024-05-27 08:50:25 +02:00
|
|
|
fatal_error = net["r{}".format(i)].checkRouterRunning()
|
2017-05-20 05:41:20 +02:00
|
|
|
assert fatal_error == "", fatal_error
|
|
|
|
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
def test_isis_interfaces():
|
|
|
|
global fatal_error
|
2021-07-29 11:38:55 +02:00
|
|
|
net = get_topogen().net
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
# Skip if previous fatal error condition is raised
|
|
|
|
if fatal_error != "":
|
|
|
|
pytest.skip(fatal_error)
|
|
|
|
|
|
|
|
thisDir = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
|
2017-04-26 18:18:58 +02:00
|
|
|
print("\n\n** Verifying ISIS interfaces")
|
2017-04-08 12:49:43 +02:00
|
|
|
print("******************************************\n")
|
|
|
|
failures = 0
|
|
|
|
for i in range(1, 2):
|
2024-05-27 08:50:25 +02:00
|
|
|
refTableFile = "{}/r{}/show_isis_interface_detail.ref".format(thisDir, i)
|
2017-04-08 12:49:43 +02:00
|
|
|
if os.path.isfile(refTableFile):
|
|
|
|
# Read expected result from file
|
2025-04-01 08:36:15 +02:00
|
|
|
with open(refTableFile) as file:
|
|
|
|
expected = file.read().rstrip()
|
2017-04-08 12:49:43 +02:00
|
|
|
# Fix newlines (make them all the same)
|
|
|
|
expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
|
|
|
|
|
|
|
|
# Actual output from router
|
|
|
|
actual = (
|
2024-05-27 08:50:25 +02:00
|
|
|
net["r{}".format(i)]
|
2017-04-08 12:49:43 +02:00
|
|
|
.cmd('vtysh -c "show isis interface detail" 2> /dev/null')
|
|
|
|
.rstrip()
|
2020-10-07 23:22:26 +02:00
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
# Mask out Link-Local mac address portion. They are random...
|
|
|
|
actual = re.sub(r"fe80::[0-9a-f:]+", "fe80::XXXX:XXXX:XXXX:XXXX", actual)
|
|
|
|
# Mask out SNPA mac address portion. They are random...
|
|
|
|
actual = re.sub(r"SNPA: [0-9a-f\.]+", "SNPA: XXXX.XXXX.XXXX", actual)
|
2017-09-19 03:26:59 +02:00
|
|
|
# Mask out Circuit ID number
|
2020-10-01 16:21:03 +02:00
|
|
|
actual = re.sub(r"Circuit Id: 0x[0-9a-f]+", "Circuit Id: 0xXX", actual)
|
2017-04-08 12:49:43 +02:00
|
|
|
# Fix newlines (make them all the same)
|
|
|
|
actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
|
|
|
|
|
|
|
|
# Generate Diff
|
2017-05-31 04:39:21 +02:00
|
|
|
diff = topotest.get_textdiff(
|
|
|
|
actual,
|
|
|
|
expected,
|
|
|
|
title1="actual SHOW ISIS INTERFACE DETAIL",
|
|
|
|
title2="expected SHOW ISIS OSPF6 INTERFACE DETAIL",
|
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
# Empty string if it matches, otherwise diff contains unified diff
|
|
|
|
if diff:
|
|
|
|
sys.stderr.write(
|
2024-05-27 08:50:25 +02:00
|
|
|
"r{} failed SHOW ISIS INTERFACE DETAIL check:\n{}\n".format(i, diff)
|
2020-10-07 23:22:26 +02:00
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
failures += 1
|
|
|
|
else:
|
2024-05-27 08:50:25 +02:00
|
|
|
print("r{} ok".format(i))
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
assert (
|
|
|
|
failures == 0
|
2024-05-27 08:50:25 +02:00
|
|
|
), "SHOW ISIS INTERFACE DETAIL failed for router r{}:\n{}".format(i, diff)
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2017-05-20 05:41:20 +02:00
|
|
|
# Make sure that all daemons are running
|
|
|
|
for i in range(1, 2):
|
2024-05-27 08:50:25 +02:00
|
|
|
fatal_error = net["r{}".format(i)].checkRouterRunning()
|
2017-05-20 05:41:20 +02:00
|
|
|
assert fatal_error == "", fatal_error
|
|
|
|
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
def test_bgp_summary():
|
|
|
|
global fatal_error
|
2021-07-29 11:38:55 +02:00
|
|
|
net = get_topogen().net
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
# Skip if previous fatal error condition is raised
|
|
|
|
if fatal_error != "":
|
|
|
|
pytest.skip(fatal_error)
|
|
|
|
|
|
|
|
thisDir = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
|
2017-04-26 18:18:58 +02:00
|
|
|
print("\n\n** Verifying BGP Summary")
|
2017-04-08 12:49:43 +02:00
|
|
|
print("******************************************\n")
|
|
|
|
failures = 0
|
|
|
|
for i in range(1, 2):
|
2024-05-27 08:50:25 +02:00
|
|
|
refTableFile = "{}/r{}/show_ip_bgp_summary.ref".format(thisDir, i)
|
2017-04-08 12:49:43 +02:00
|
|
|
if os.path.isfile(refTableFile):
|
|
|
|
# Read expected result from file
|
2025-04-01 08:36:15 +02:00
|
|
|
with open(refTableFile) as file:
|
|
|
|
expected_original = file.read().rstrip()
|
2021-05-12 17:17:56 +02:00
|
|
|
|
2021-09-03 14:47:30 +02:00
|
|
|
for arguments in [
|
|
|
|
"",
|
|
|
|
"remote-as internal",
|
|
|
|
"remote-as external",
|
|
|
|
"remote-as 100",
|
|
|
|
"remote-as 123",
|
|
|
|
"neighbor 192.168.7.10",
|
|
|
|
"neighbor 192.168.7.10",
|
|
|
|
"neighbor fc00:0:0:8::1000",
|
|
|
|
"neighbor 10.0.0.1",
|
|
|
|
"terse",
|
|
|
|
"remote-as internal terse",
|
|
|
|
"remote-as external terse",
|
|
|
|
"remote-as 100 terse",
|
|
|
|
"remote-as 123 terse",
|
|
|
|
"neighbor 192.168.7.10 terse",
|
|
|
|
"neighbor 192.168.7.10 terse",
|
|
|
|
"neighbor fc00:0:0:8::1000 terse",
|
|
|
|
"neighbor 10.0.0.1 terse",
|
|
|
|
]:
|
2021-05-12 17:17:56 +02:00
|
|
|
# Actual output from router
|
|
|
|
actual = (
|
2024-05-27 08:50:25 +02:00
|
|
|
net["r{}".format(i)]
|
2021-09-03 14:47:30 +02:00
|
|
|
.cmd(
|
|
|
|
'vtysh -c "show ip bgp summary ' + arguments + '" 2> /dev/null'
|
|
|
|
)
|
2021-05-12 17:17:56 +02:00
|
|
|
.rstrip()
|
|
|
|
)
|
bgpd: add terse display option on show bgp summary
Add a terse option to show bgp summary to shorten output.
Do not show the following information about the BGP
instances: the number of RIB entries, the table version and the used memory.
The "terse" option can be used in combination with the "remote-as", "neighbor",
"failed" and "established" filters, and with the "wide" option as well.
Before patch:
ubuntu# show bgp summary remote-as 123456
IPv4 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
BGP table version 0
RIB entries 3, using 552 bytes of memory
Peers 5, using 3635 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
10.200.200.2 4 123456 81432 4 0 56092 0 00:00:13 572106 0 N/A
Displayed neighbors 1
Total number of neighbors 4
IPv6 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
BGP table version 0
RIB entries 3, using 552 bytes of memory
Peers 5, using 3635 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
% No matching neighbor
Total number of neighbors 5
After patch:
ubuntu# show bgp summary remote-as 123456 terse
IPv4 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
10.200.200.2 4 123456 81432 4 0 56092 0 00:00:13 572106 0 N/A
Displayed neighbors 1
Total number of neighbors 4
IPv6 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 1
% No matching neighbor
Signed-off-by: Louis Scalbert <louis.scalbert@6wind.com>
2021-05-12 18:54:11 +02:00
|
|
|
|
2021-05-12 17:17:56 +02:00
|
|
|
# Mask out "using XXiXX bytes" portion. They are random...
|
|
|
|
actual = re.sub(r"using [0-9]+ bytes", "using XXXX bytes", actual)
|
|
|
|
# Mask out "using XiXXX KiB" portion. They are random...
|
|
|
|
actual = re.sub(r"using [0-9]+ KiB", "using XXXX KiB", actual)
|
|
|
|
|
|
|
|
# Remove extra summaries which exist with newer versions
|
|
|
|
|
|
|
|
# Remove summary lines (changed recently)
|
|
|
|
actual = re.sub(r"Total number.*", "", actual)
|
|
|
|
actual = re.sub(r"Displayed.*", "", actual)
|
|
|
|
# Remove IPv4 Unicast Summary (Title only)
|
2023-12-14 14:09:39 +01:00
|
|
|
actual = re.sub(r"IPv4 Unicast Summary:", "", actual)
|
2021-05-12 17:17:56 +02:00
|
|
|
# Remove IPv4 Multicast Summary (all of it)
|
2023-12-14 14:09:39 +01:00
|
|
|
actual = re.sub(r"IPv4 Multicast Summary:", "", actual)
|
2021-05-12 17:17:56 +02:00
|
|
|
actual = re.sub(r"No IPv4 Multicast neighbor is configured", "", actual)
|
|
|
|
# Remove IPv4 VPN Summary (all of it)
|
2023-12-14 14:09:39 +01:00
|
|
|
actual = re.sub(r"IPv4 VPN Summary:", "", actual)
|
2021-05-12 17:17:56 +02:00
|
|
|
actual = re.sub(r"No IPv4 VPN neighbor is configured", "", actual)
|
|
|
|
# Remove IPv4 Encap Summary (all of it)
|
2023-12-14 14:09:39 +01:00
|
|
|
actual = re.sub(r"IPv4 Encap Summary:", "", actual)
|
2021-05-12 17:17:56 +02:00
|
|
|
actual = re.sub(r"No IPv4 Encap neighbor is configured", "", actual)
|
|
|
|
# Remove Unknown Summary (all of it)
|
2023-12-14 14:09:39 +01:00
|
|
|
actual = re.sub(r"Unknown Summary:", "", actual)
|
2021-05-12 17:17:56 +02:00
|
|
|
actual = re.sub(r"No Unknown neighbor is configured", "", actual)
|
2021-09-08 22:18:52 +02:00
|
|
|
# Make Connect/Active/Idle the same (change them all to Active)
|
|
|
|
actual = re.sub(r" Connect ", " Active ", actual)
|
|
|
|
actual = re.sub(r" Idle ", " Active ", actual)
|
2021-05-12 17:17:56 +02:00
|
|
|
|
2023-12-14 14:09:39 +01:00
|
|
|
actual = re.sub(r"IPv4 labeled-unicast Summary:", "", actual)
|
2021-05-12 17:17:56 +02:00
|
|
|
actual = re.sub(
|
|
|
|
r"No IPv4 labeled-unicast neighbor is configured", "", actual
|
|
|
|
)
|
2017-04-26 18:15:39 +02:00
|
|
|
|
2021-05-12 17:17:56 +02:00
|
|
|
expected = expected_original
|
bgpd: add terse display option on show bgp summary
Add a terse option to show bgp summary to shorten output.
Do not show the following information about the BGP
instances: the number of RIB entries, the table version and the used memory.
The "terse" option can be used in combination with the "remote-as", "neighbor",
"failed" and "established" filters, and with the "wide" option as well.
Before patch:
ubuntu# show bgp summary remote-as 123456
IPv4 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
BGP table version 0
RIB entries 3, using 552 bytes of memory
Peers 5, using 3635 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
10.200.200.2 4 123456 81432 4 0 56092 0 00:00:13 572106 0 N/A
Displayed neighbors 1
Total number of neighbors 4
IPv6 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
BGP table version 0
RIB entries 3, using 552 bytes of memory
Peers 5, using 3635 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
% No matching neighbor
Total number of neighbors 5
After patch:
ubuntu# show bgp summary remote-as 123456 terse
IPv4 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
10.200.200.2 4 123456 81432 4 0 56092 0 00:00:13 572106 0 N/A
Displayed neighbors 1
Total number of neighbors 4
IPv6 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 1
% No matching neighbor
Signed-off-by: Louis Scalbert <louis.scalbert@6wind.com>
2021-05-12 18:54:11 +02:00
|
|
|
# apply argumentss on expected output
|
|
|
|
if "internal" in arguments or "remote-as 100" in arguments:
|
2021-05-12 17:17:56 +02:00
|
|
|
expected = re.sub(r".+\s+200\s+.+", "", expected)
|
bgpd: add terse display option on show bgp summary
Add a terse option to show bgp summary to shorten output.
Do not show the following information about the BGP
instances: the number of RIB entries, the table version and the used memory.
The "terse" option can be used in combination with the "remote-as", "neighbor",
"failed" and "established" filters, and with the "wide" option as well.
Before patch:
ubuntu# show bgp summary remote-as 123456
IPv4 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
BGP table version 0
RIB entries 3, using 552 bytes of memory
Peers 5, using 3635 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
10.200.200.2 4 123456 81432 4 0 56092 0 00:00:13 572106 0 N/A
Displayed neighbors 1
Total number of neighbors 4
IPv6 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
BGP table version 0
RIB entries 3, using 552 bytes of memory
Peers 5, using 3635 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
% No matching neighbor
Total number of neighbors 5
After patch:
ubuntu# show bgp summary remote-as 123456 terse
IPv4 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
10.200.200.2 4 123456 81432 4 0 56092 0 00:00:13 572106 0 N/A
Displayed neighbors 1
Total number of neighbors 4
IPv6 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 1
% No matching neighbor
Signed-off-by: Louis Scalbert <louis.scalbert@6wind.com>
2021-05-12 18:54:11 +02:00
|
|
|
elif "external" in arguments:
|
2021-05-12 17:17:56 +02:00
|
|
|
expected = re.sub(r".+\s+100\s+.+Active.+", "", expected)
|
bgpd: add terse display option on show bgp summary
Add a terse option to show bgp summary to shorten output.
Do not show the following information about the BGP
instances: the number of RIB entries, the table version and the used memory.
The "terse" option can be used in combination with the "remote-as", "neighbor",
"failed" and "established" filters, and with the "wide" option as well.
Before patch:
ubuntu# show bgp summary remote-as 123456
IPv4 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
BGP table version 0
RIB entries 3, using 552 bytes of memory
Peers 5, using 3635 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
10.200.200.2 4 123456 81432 4 0 56092 0 00:00:13 572106 0 N/A
Displayed neighbors 1
Total number of neighbors 4
IPv6 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
BGP table version 0
RIB entries 3, using 552 bytes of memory
Peers 5, using 3635 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
% No matching neighbor
Total number of neighbors 5
After patch:
ubuntu# show bgp summary remote-as 123456 terse
IPv4 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
10.200.200.2 4 123456 81432 4 0 56092 0 00:00:13 572106 0 N/A
Displayed neighbors 1
Total number of neighbors 4
IPv6 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 1
% No matching neighbor
Signed-off-by: Louis Scalbert <louis.scalbert@6wind.com>
2021-05-12 18:54:11 +02:00
|
|
|
elif "remote-as 123" in arguments:
|
2021-05-12 17:17:56 +02:00
|
|
|
expected = re.sub(
|
|
|
|
r"(192.168.7.(1|2)0|fc00:0:0:8::(1|2)000).+Active.+",
|
2021-09-03 14:47:30 +02:00
|
|
|
"",
|
|
|
|
expected,
|
2021-05-12 17:17:56 +02:00
|
|
|
)
|
bgpd: add terse display option on show bgp summary
Add a terse option to show bgp summary to shorten output.
Do not show the following information about the BGP
instances: the number of RIB entries, the table version and the used memory.
The "terse" option can be used in combination with the "remote-as", "neighbor",
"failed" and "established" filters, and with the "wide" option as well.
Before patch:
ubuntu# show bgp summary remote-as 123456
IPv4 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
BGP table version 0
RIB entries 3, using 552 bytes of memory
Peers 5, using 3635 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
10.200.200.2 4 123456 81432 4 0 56092 0 00:00:13 572106 0 N/A
Displayed neighbors 1
Total number of neighbors 4
IPv6 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
BGP table version 0
RIB entries 3, using 552 bytes of memory
Peers 5, using 3635 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
% No matching neighbor
Total number of neighbors 5
After patch:
ubuntu# show bgp summary remote-as 123456 terse
IPv4 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
10.200.200.2 4 123456 81432 4 0 56092 0 00:00:13 572106 0 N/A
Displayed neighbors 1
Total number of neighbors 4
IPv6 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 1
% No matching neighbor
Signed-off-by: Louis Scalbert <louis.scalbert@6wind.com>
2021-05-12 18:54:11 +02:00
|
|
|
expected = re.sub(r"\nNeighbor.+Desc", "", expected)
|
2021-05-12 17:40:03 +02:00
|
|
|
expected = expected + "% No matching neighbor\n"
|
bgpd: add terse display option on show bgp summary
Add a terse option to show bgp summary to shorten output.
Do not show the following information about the BGP
instances: the number of RIB entries, the table version and the used memory.
The "terse" option can be used in combination with the "remote-as", "neighbor",
"failed" and "established" filters, and with the "wide" option as well.
Before patch:
ubuntu# show bgp summary remote-as 123456
IPv4 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
BGP table version 0
RIB entries 3, using 552 bytes of memory
Peers 5, using 3635 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
10.200.200.2 4 123456 81432 4 0 56092 0 00:00:13 572106 0 N/A
Displayed neighbors 1
Total number of neighbors 4
IPv6 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
BGP table version 0
RIB entries 3, using 552 bytes of memory
Peers 5, using 3635 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
% No matching neighbor
Total number of neighbors 5
After patch:
ubuntu# show bgp summary remote-as 123456 terse
IPv4 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
10.200.200.2 4 123456 81432 4 0 56092 0 00:00:13 572106 0 N/A
Displayed neighbors 1
Total number of neighbors 4
IPv6 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 1
% No matching neighbor
Signed-off-by: Louis Scalbert <louis.scalbert@6wind.com>
2021-05-12 18:54:11 +02:00
|
|
|
elif "192.168.7.10" in arguments:
|
2021-05-12 17:17:56 +02:00
|
|
|
expected = re.sub(
|
2021-09-03 14:47:30 +02:00
|
|
|
r"(192.168.7.20|fc00:0:0:8::(1|2)000).+Active.+", "", expected
|
2021-05-12 17:17:56 +02:00
|
|
|
)
|
bgpd: add terse display option on show bgp summary
Add a terse option to show bgp summary to shorten output.
Do not show the following information about the BGP
instances: the number of RIB entries, the table version and the used memory.
The "terse" option can be used in combination with the "remote-as", "neighbor",
"failed" and "established" filters, and with the "wide" option as well.
Before patch:
ubuntu# show bgp summary remote-as 123456
IPv4 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
BGP table version 0
RIB entries 3, using 552 bytes of memory
Peers 5, using 3635 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
10.200.200.2 4 123456 81432 4 0 56092 0 00:00:13 572106 0 N/A
Displayed neighbors 1
Total number of neighbors 4
IPv6 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
BGP table version 0
RIB entries 3, using 552 bytes of memory
Peers 5, using 3635 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
% No matching neighbor
Total number of neighbors 5
After patch:
ubuntu# show bgp summary remote-as 123456 terse
IPv4 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
10.200.200.2 4 123456 81432 4 0 56092 0 00:00:13 572106 0 N/A
Displayed neighbors 1
Total number of neighbors 4
IPv6 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 1
% No matching neighbor
Signed-off-by: Louis Scalbert <louis.scalbert@6wind.com>
2021-05-12 18:54:11 +02:00
|
|
|
elif "fc00:0:0:8::1000" in arguments:
|
2021-05-12 17:17:56 +02:00
|
|
|
expected = re.sub(
|
2021-09-03 14:47:30 +02:00
|
|
|
r"(192.168.7.(1|2)0|fc00:0:0:8::2000).+Active.+", "", expected
|
2021-05-12 17:17:56 +02:00
|
|
|
)
|
bgpd: add terse display option on show bgp summary
Add a terse option to show bgp summary to shorten output.
Do not show the following information about the BGP
instances: the number of RIB entries, the table version and the used memory.
The "terse" option can be used in combination with the "remote-as", "neighbor",
"failed" and "established" filters, and with the "wide" option as well.
Before patch:
ubuntu# show bgp summary remote-as 123456
IPv4 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
BGP table version 0
RIB entries 3, using 552 bytes of memory
Peers 5, using 3635 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
10.200.200.2 4 123456 81432 4 0 56092 0 00:00:13 572106 0 N/A
Displayed neighbors 1
Total number of neighbors 4
IPv6 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
BGP table version 0
RIB entries 3, using 552 bytes of memory
Peers 5, using 3635 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
% No matching neighbor
Total number of neighbors 5
After patch:
ubuntu# show bgp summary remote-as 123456 terse
IPv4 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
10.200.200.2 4 123456 81432 4 0 56092 0 00:00:13 572106 0 N/A
Displayed neighbors 1
Total number of neighbors 4
IPv6 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 1
% No matching neighbor
Signed-off-by: Louis Scalbert <louis.scalbert@6wind.com>
2021-05-12 18:54:11 +02:00
|
|
|
elif "10.0.0.1" in arguments:
|
2021-10-07 17:27:29 +02:00
|
|
|
expected = "No such neighbor in this view/vrf"
|
2021-05-12 17:17:56 +02:00
|
|
|
|
bgpd: add terse display option on show bgp summary
Add a terse option to show bgp summary to shorten output.
Do not show the following information about the BGP
instances: the number of RIB entries, the table version and the used memory.
The "terse" option can be used in combination with the "remote-as", "neighbor",
"failed" and "established" filters, and with the "wide" option as well.
Before patch:
ubuntu# show bgp summary remote-as 123456
IPv4 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
BGP table version 0
RIB entries 3, using 552 bytes of memory
Peers 5, using 3635 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
10.200.200.2 4 123456 81432 4 0 56092 0 00:00:13 572106 0 N/A
Displayed neighbors 1
Total number of neighbors 4
IPv6 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
BGP table version 0
RIB entries 3, using 552 bytes of memory
Peers 5, using 3635 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
% No matching neighbor
Total number of neighbors 5
After patch:
ubuntu# show bgp summary remote-as 123456 terse
IPv4 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
10.200.200.2 4 123456 81432 4 0 56092 0 00:00:13 572106 0 N/A
Displayed neighbors 1
Total number of neighbors 4
IPv6 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 1
% No matching neighbor
Signed-off-by: Louis Scalbert <louis.scalbert@6wind.com>
2021-05-12 18:54:11 +02:00
|
|
|
if "terse" in arguments:
|
|
|
|
expected = re.sub(r"BGP table version .+", "", expected)
|
|
|
|
expected = re.sub(r"RIB entries .+", "", expected)
|
|
|
|
expected = re.sub(r"Peers [0-9]+, using .+", "", expected)
|
|
|
|
|
2021-05-12 17:17:56 +02:00
|
|
|
# Strip empty lines
|
|
|
|
actual = actual.lstrip().rstrip()
|
|
|
|
expected = expected.lstrip().rstrip()
|
|
|
|
actual = re.sub(r"\n+", "\n", actual)
|
|
|
|
expected = re.sub(r"\n+", "\n", expected)
|
|
|
|
|
|
|
|
# reapply initial formatting
|
bgpd: add terse display option on show bgp summary
Add a terse option to show bgp summary to shorten output.
Do not show the following information about the BGP
instances: the number of RIB entries, the table version and the used memory.
The "terse" option can be used in combination with the "remote-as", "neighbor",
"failed" and "established" filters, and with the "wide" option as well.
Before patch:
ubuntu# show bgp summary remote-as 123456
IPv4 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
BGP table version 0
RIB entries 3, using 552 bytes of memory
Peers 5, using 3635 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
10.200.200.2 4 123456 81432 4 0 56092 0 00:00:13 572106 0 N/A
Displayed neighbors 1
Total number of neighbors 4
IPv6 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
BGP table version 0
RIB entries 3, using 552 bytes of memory
Peers 5, using 3635 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
% No matching neighbor
Total number of neighbors 5
After patch:
ubuntu# show bgp summary remote-as 123456 terse
IPv4 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
10.200.200.2 4 123456 81432 4 0 56092 0 00:00:13 572106 0 N/A
Displayed neighbors 1
Total number of neighbors 4
IPv6 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 1
% No matching neighbor
Signed-off-by: Louis Scalbert <louis.scalbert@6wind.com>
2021-05-12 18:54:11 +02:00
|
|
|
if "terse" in arguments:
|
|
|
|
actual = re.sub(r" vrf-id 0\n", " vrf-id 0\n\n", actual)
|
|
|
|
expected = re.sub(r" vrf-id 0\n", " vrf-id 0\n\n", expected)
|
|
|
|
else:
|
|
|
|
actual = re.sub(r"KiB of memory\n", "KiB of memory\n\n", actual)
|
|
|
|
expected = re.sub(r"KiB of memory\n", "KiB of memory\n\n", expected)
|
2021-05-12 17:17:56 +02:00
|
|
|
|
|
|
|
# realign expected neighbor columns if needed
|
|
|
|
try:
|
2021-09-03 14:47:30 +02:00
|
|
|
idx_actual = (
|
|
|
|
re.search(r"(Neighbor\s+V\s+)", actual).group(1).find("V")
|
|
|
|
)
|
|
|
|
idx_expected = (
|
|
|
|
re.search(r"(Neighbor\s+V\s+)", expected).group(1).find("V")
|
|
|
|
)
|
2021-05-12 17:17:56 +02:00
|
|
|
idx_diff = idx_expected - idx_actual
|
|
|
|
if idx_diff > 0:
|
|
|
|
# Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
|
|
|
|
expected = re.sub(" " * idx_diff + "V ", "V ", expected)
|
|
|
|
# 192.168.7.10 4 100 0 0 0 0 0 never Active
|
|
|
|
expected = re.sub(" " * idx_diff + "4 ", "4 ", expected)
|
|
|
|
except AttributeError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
# Fix newlines (make them all the same)
|
|
|
|
actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
|
|
|
|
expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
|
|
|
|
|
|
|
|
# Generate Diff
|
|
|
|
diff = topotest.get_textdiff(
|
|
|
|
actual,
|
|
|
|
expected,
|
2021-09-03 14:47:30 +02:00
|
|
|
title1="actual SHOW IP BGP SUMMARY " + arguments.upper(),
|
bgpd: add terse display option on show bgp summary
Add a terse option to show bgp summary to shorten output.
Do not show the following information about the BGP
instances: the number of RIB entries, the table version and the used memory.
The "terse" option can be used in combination with the "remote-as", "neighbor",
"failed" and "established" filters, and with the "wide" option as well.
Before patch:
ubuntu# show bgp summary remote-as 123456
IPv4 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
BGP table version 0
RIB entries 3, using 552 bytes of memory
Peers 5, using 3635 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
10.200.200.2 4 123456 81432 4 0 56092 0 00:00:13 572106 0 N/A
Displayed neighbors 1
Total number of neighbors 4
IPv6 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
BGP table version 0
RIB entries 3, using 552 bytes of memory
Peers 5, using 3635 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
% No matching neighbor
Total number of neighbors 5
After patch:
ubuntu# show bgp summary remote-as 123456 terse
IPv4 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 0
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
10.200.200.2 4 123456 81432 4 0 56092 0 00:00:13 572106 0 N/A
Displayed neighbors 1
Total number of neighbors 4
IPv6 Unicast Summary (VRF default):
BGP router identifier X.X.X.X, local AS number XXX vrf-id 1
% No matching neighbor
Signed-off-by: Louis Scalbert <louis.scalbert@6wind.com>
2021-05-12 18:54:11 +02:00
|
|
|
title2="expected SHOW IP BGP SUMMARY " + arguments.upper(),
|
2021-05-12 17:17:56 +02:00
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2021-05-12 17:17:56 +02:00
|
|
|
# Empty string if it matches, otherwise diff contains unified diff
|
|
|
|
if diff:
|
|
|
|
sys.stderr.write(
|
2024-05-27 08:50:25 +02:00
|
|
|
"r{} failed SHOW IP BGP SUMMARY check:\n{}\n".format(i, diff)
|
2021-05-12 17:17:56 +02:00
|
|
|
)
|
|
|
|
failures += 1
|
|
|
|
else:
|
2024-05-27 08:50:25 +02:00
|
|
|
print("r{} ok".format(i))
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2021-09-03 14:47:30 +02:00
|
|
|
assert (
|
|
|
|
failures == 0
|
2024-05-27 08:50:25 +02:00
|
|
|
), "SHOW IP BGP SUMMARY failed for router r{}:\n{}".format(i, diff)
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2017-05-20 05:41:20 +02:00
|
|
|
# Make sure that all daemons are running
|
|
|
|
for i in range(1, 2):
|
2024-05-27 08:50:25 +02:00
|
|
|
fatal_error = net["r{}".format(i)].checkRouterRunning()
|
2017-05-20 05:41:20 +02:00
|
|
|
assert fatal_error == "", fatal_error
|
|
|
|
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
def test_bgp_ipv6_summary():
|
|
|
|
global fatal_error
|
2021-07-29 11:38:55 +02:00
|
|
|
net = get_topogen().net
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
# Skip if previous fatal error condition is raised
|
|
|
|
if fatal_error != "":
|
|
|
|
pytest.skip(fatal_error)
|
|
|
|
|
|
|
|
thisDir = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
|
2017-04-26 18:18:58 +02:00
|
|
|
print("\n\n** Verifying BGP IPv6 Summary")
|
2017-04-08 12:49:43 +02:00
|
|
|
print("******************************************\n")
|
|
|
|
failures = 0
|
|
|
|
for i in range(1, 2):
|
2024-05-27 08:50:25 +02:00
|
|
|
refTableFile = "{}/r{}/show_bgp_ipv6_summary.ref".format(thisDir, i)
|
2017-04-08 12:49:43 +02:00
|
|
|
if os.path.isfile(refTableFile):
|
|
|
|
# Read expected result from file
|
2025-04-01 08:36:15 +02:00
|
|
|
with open(refTableFile) as file:
|
|
|
|
expected = file.read().rstrip()
|
2017-04-08 12:49:43 +02:00
|
|
|
# Fix newlines (make them all the same)
|
|
|
|
expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
|
|
|
|
|
|
|
|
# Actual output from router
|
|
|
|
actual = (
|
2024-05-27 08:50:25 +02:00
|
|
|
net["r{}".format(i)]
|
2017-04-08 12:49:43 +02:00
|
|
|
.cmd('vtysh -c "show bgp ipv6 summary" 2> /dev/null')
|
|
|
|
.rstrip()
|
2020-10-07 23:22:26 +02:00
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
# Mask out "using XXiXX bytes" portion. They are random...
|
|
|
|
actual = re.sub(r"using [0-9]+ bytes", "using XXXX bytes", actual)
|
|
|
|
# Mask out "using XiXXX KiB" portion. They are random...
|
|
|
|
actual = re.sub(r"using [0-9]+ KiB", "using XXXX KiB", actual)
|
2017-04-11 06:07:02 +02:00
|
|
|
#
|
|
|
|
# Remove extra summaries which exist with newer versions
|
|
|
|
#
|
|
|
|
# Remove summary lines (changed recently)
|
|
|
|
actual = re.sub(r"Total number.*", "", actual)
|
|
|
|
actual = re.sub(r"Displayed.*", "", actual)
|
|
|
|
# Remove IPv4 Unicast Summary (Title only)
|
2023-12-14 14:09:39 +01:00
|
|
|
actual = re.sub(r"IPv6 Unicast Summary:", "", actual)
|
2017-04-11 06:07:02 +02:00
|
|
|
# Remove IPv4 Multicast Summary (all of it)
|
2023-12-14 14:09:39 +01:00
|
|
|
actual = re.sub(r"IPv6 Multicast Summary:", "", actual)
|
2017-04-11 06:07:02 +02:00
|
|
|
actual = re.sub(r"No IPv6 Multicast neighbor is configured", "", actual)
|
|
|
|
# Remove IPv4 VPN Summary (all of it)
|
2023-12-14 14:09:39 +01:00
|
|
|
actual = re.sub(r"IPv6 VPN Summary:", "", actual)
|
2017-04-11 06:07:02 +02:00
|
|
|
actual = re.sub(r"No IPv6 VPN neighbor is configured", "", actual)
|
|
|
|
# Remove IPv4 Encap Summary (all of it)
|
2023-12-14 14:09:39 +01:00
|
|
|
actual = re.sub(r"IPv6 Encap Summary:", "", actual)
|
2017-04-11 06:07:02 +02:00
|
|
|
actual = re.sub(r"No IPv6 Encap neighbor is configured", "", actual)
|
|
|
|
# Remove Unknown Summary (all of it)
|
2023-12-14 14:09:39 +01:00
|
|
|
actual = re.sub(r"Unknown Summary:", "", actual)
|
2017-04-11 06:07:02 +02:00
|
|
|
actual = re.sub(r"No Unknown neighbor is configured", "", actual)
|
2021-09-08 22:18:52 +02:00
|
|
|
# Make Connect/Active/Idle the same (change them all to Active)
|
|
|
|
actual = re.sub(r" Connect ", " Active ", actual)
|
|
|
|
actual = re.sub(r" Idle ", " Active ", actual)
|
2017-04-26 18:15:39 +02:00
|
|
|
|
|
|
|
# Remove Labeled Unicast Summary (all of it)
|
2023-12-14 14:09:39 +01:00
|
|
|
actual = re.sub(r"IPv6 labeled-unicast Summary:", "", actual)
|
2017-04-26 18:15:39 +02:00
|
|
|
actual = re.sub(
|
|
|
|
r"No IPv6 labeled-unicast neighbor is configured", "", actual
|
|
|
|
)
|
|
|
|
|
2017-04-11 06:07:02 +02:00
|
|
|
# Strip empty lines
|
|
|
|
actual = actual.lstrip()
|
|
|
|
actual = actual.rstrip()
|
|
|
|
#
|
2017-04-08 12:49:43 +02:00
|
|
|
# Fix newlines (make them all the same)
|
|
|
|
actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
|
|
|
|
|
|
|
|
# Generate Diff
|
2017-05-31 04:39:21 +02:00
|
|
|
diff = topotest.get_textdiff(
|
|
|
|
actual,
|
|
|
|
expected,
|
|
|
|
title1="actual SHOW BGP IPv6 SUMMARY",
|
|
|
|
title2="expected SHOW BGP IPv6 SUMMARY",
|
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
# Empty string if it matches, otherwise diff contains unified diff
|
|
|
|
if diff:
|
|
|
|
sys.stderr.write(
|
2024-05-27 08:50:25 +02:00
|
|
|
"r{} failed SHOW BGP IPv6 SUMMARY check:\n{}\n".format(i, diff)
|
2020-10-07 23:22:26 +02:00
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
failures += 1
|
|
|
|
else:
|
2024-05-27 08:50:25 +02:00
|
|
|
print("r{} ok".format(i))
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2024-05-27 08:50:25 +02:00
|
|
|
assert (
|
|
|
|
failures == 0
|
|
|
|
), "SHOW BGP IPv6 SUMMARY failed for router r{}:\n{}".format(i, diff)
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2017-05-20 05:41:20 +02:00
|
|
|
# Make sure that all daemons are running
|
|
|
|
for i in range(1, 2):
|
2024-05-27 08:50:25 +02:00
|
|
|
fatal_error = net["r{}".format(i)].checkRouterRunning()
|
2017-05-20 05:41:20 +02:00
|
|
|
assert fatal_error == "", fatal_error
|
|
|
|
|
2020-12-17 21:32:11 +01:00
|
|
|
|
2020-12-08 21:50:46 +01:00
|
|
|
def test_nht():
|
2021-10-06 19:49:52 +02:00
|
|
|
global fatal_error
|
2021-07-29 11:38:55 +02:00
|
|
|
net = get_topogen().net
|
2021-10-06 19:49:52 +02:00
|
|
|
|
|
|
|
# Skip if previous fatal error condition is raised
|
|
|
|
if fatal_error != "":
|
|
|
|
pytest.skip(fatal_error)
|
|
|
|
|
2020-12-08 21:50:46 +01:00
|
|
|
print("\n\n**** Test that nexthop tracking is at least nominally working ****\n")
|
|
|
|
|
|
|
|
thisDir = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
|
|
|
|
for i in range(1, 2):
|
2024-05-27 08:50:25 +02:00
|
|
|
nhtFile = "{}/r{}/ip_nht.ref".format(thisDir, i)
|
2025-04-01 08:36:15 +02:00
|
|
|
with open(nhtFile) as file:
|
|
|
|
expected = file.read().rstrip()
|
2020-12-08 21:50:46 +01:00
|
|
|
expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
|
|
|
|
|
2024-05-27 08:50:25 +02:00
|
|
|
actual = (
|
|
|
|
net["r{}".format(i)].cmd('vtysh -c "show ip nht" 2> /dev/null').rstrip()
|
|
|
|
)
|
2021-08-27 20:28:29 +02:00
|
|
|
actual = re.sub(r"fd [0-9]+", "fd XX", actual)
|
2020-12-08 21:50:46 +01:00
|
|
|
actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
|
|
|
|
|
|
|
|
diff = topotest.get_textdiff(
|
|
|
|
actual,
|
|
|
|
expected,
|
|
|
|
title1="Actual `show ip nht`",
|
|
|
|
title2="Expected `show ip nht`",
|
|
|
|
)
|
|
|
|
|
|
|
|
if diff:
|
2024-05-27 08:50:25 +02:00
|
|
|
assert 0, "r{} failed ip nht check:\n{}\n".format(i, diff)
|
2020-12-08 21:50:46 +01:00
|
|
|
else:
|
|
|
|
print("show ip nht is ok\n")
|
|
|
|
|
2024-05-27 08:50:25 +02:00
|
|
|
nhtFile = "{}/r{}/ipv6_nht.ref".format(thisDir, i)
|
2025-04-01 08:36:15 +02:00
|
|
|
with open(nhtFile) as file:
|
|
|
|
expected = file.read().rstrip()
|
2020-12-08 21:50:46 +01:00
|
|
|
expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
|
|
|
|
|
2024-05-27 08:50:25 +02:00
|
|
|
actual = (
|
|
|
|
net["r{}".format(i)].cmd('vtysh -c "show ipv6 nht" 2> /dev/null').rstrip()
|
|
|
|
)
|
2021-08-27 20:28:29 +02:00
|
|
|
actual = re.sub(r"fd [0-9]+", "fd XX", actual)
|
2020-12-08 21:50:46 +01:00
|
|
|
actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
|
|
|
|
|
|
|
|
diff = topotest.get_textdiff(
|
|
|
|
actual,
|
|
|
|
expected,
|
|
|
|
title1="Actual `show ip nht`",
|
|
|
|
title2="Expected `show ip nht`",
|
|
|
|
)
|
|
|
|
|
|
|
|
if diff:
|
2024-05-27 08:50:25 +02:00
|
|
|
assert 0, "r{} failed ipv6 nht check:\n{}\n".format(i, diff)
|
2020-12-08 21:50:46 +01:00
|
|
|
else:
|
|
|
|
print("show ipv6 nht is ok\n")
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2020-12-17 21:32:11 +01:00
|
|
|
|
2017-04-08 12:49:43 +02:00
|
|
|
def test_bgp_ipv4():
|
|
|
|
global fatal_error
|
2021-07-29 11:38:55 +02:00
|
|
|
net = get_topogen().net
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
# Skip if previous fatal error condition is raised
|
|
|
|
if fatal_error != "":
|
|
|
|
pytest.skip(fatal_error)
|
|
|
|
|
|
|
|
thisDir = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
|
2017-04-26 18:18:58 +02:00
|
|
|
print("\n\n** Verifying BGP IPv4")
|
2017-04-08 12:49:43 +02:00
|
|
|
print("******************************************\n")
|
2018-04-21 19:23:54 +02:00
|
|
|
diffresult = {}
|
2017-04-08 12:49:43 +02:00
|
|
|
for i in range(1, 2):
|
2020-09-23 23:08:36 +02:00
|
|
|
success = 0
|
2024-05-27 08:50:25 +02:00
|
|
|
for refTableFile in glob.glob("{}/r{}/show_bgp_ipv4*.ref".format(thisDir, i)):
|
2020-09-23 23:08:36 +02:00
|
|
|
if os.path.isfile(refTableFile):
|
|
|
|
# Read expected result from file
|
2025-04-01 08:36:15 +02:00
|
|
|
with open(refTableFile) as file:
|
|
|
|
expected = file.read().rstrip()
|
2020-09-23 23:08:36 +02:00
|
|
|
# Fix newlines (make them all the same)
|
|
|
|
expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
|
|
|
|
|
|
|
|
# Actual output from router
|
|
|
|
actual = (
|
2024-05-27 08:50:25 +02:00
|
|
|
net["r{}".format(i)]
|
|
|
|
.cmd('vtysh -c "show bgp ipv4" 2> /dev/null')
|
|
|
|
.rstrip()
|
2020-10-07 23:22:26 +02:00
|
|
|
)
|
2020-09-23 23:08:36 +02:00
|
|
|
# Remove summary line (changed recently)
|
|
|
|
actual = re.sub(r"Total number.*", "", actual)
|
|
|
|
actual = re.sub(r"Displayed.*", "", actual)
|
|
|
|
actual = actual.rstrip()
|
|
|
|
# Fix newlines (make them all the same)
|
|
|
|
actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
|
|
|
|
|
|
|
|
# Generate Diff
|
|
|
|
diff = topotest.get_textdiff(
|
|
|
|
actual,
|
|
|
|
expected,
|
|
|
|
title1="actual SHOW BGP IPv4",
|
|
|
|
title2="expected SHOW BGP IPv4",
|
|
|
|
)
|
|
|
|
|
|
|
|
# Empty string if it matches, otherwise diff contains unified diff
|
|
|
|
if diff:
|
|
|
|
diffresult[refTableFile] = diff
|
|
|
|
else:
|
|
|
|
success = 1
|
2024-05-27 08:50:25 +02:00
|
|
|
print("template {} matched: r{} ok".format(refTableFile, i))
|
2020-09-23 23:08:36 +02:00
|
|
|
break
|
|
|
|
|
|
|
|
if not success:
|
|
|
|
resultstr = "No template matched.\n"
|
2020-11-25 13:49:45 +01:00
|
|
|
for f in diffresult.keys():
|
2024-05-27 08:50:25 +02:00
|
|
|
resultstr += (
|
|
|
|
"template {}: r{} failed SHOW BGP IPv4 check:\n{}\n".format(
|
|
|
|
f,
|
|
|
|
i,
|
|
|
|
diffresult[f],
|
|
|
|
)
|
2020-09-23 23:08:36 +02:00
|
|
|
)
|
|
|
|
raise AssertionError(
|
2024-05-27 08:50:25 +02:00
|
|
|
"SHOW BGP IPv4 failed for router r{}:\n{}".format(i, resultstr)
|
2020-10-07 23:22:26 +02:00
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2017-05-20 05:41:20 +02:00
|
|
|
# Make sure that all daemons are running
|
|
|
|
for i in range(1, 2):
|
2024-05-27 08:50:25 +02:00
|
|
|
fatal_error = net["r{}".format(i)].checkRouterRunning()
|
2017-05-20 05:41:20 +02:00
|
|
|
assert fatal_error == "", fatal_error
|
|
|
|
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
def test_bgp_ipv6():
|
|
|
|
global fatal_error
|
2021-07-29 11:38:55 +02:00
|
|
|
net = get_topogen().net
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
# Skip if previous fatal error condition is raised
|
|
|
|
if fatal_error != "":
|
|
|
|
pytest.skip(fatal_error)
|
|
|
|
|
|
|
|
thisDir = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
|
2017-04-26 18:18:58 +02:00
|
|
|
print("\n\n** Verifying BGP IPv6")
|
2017-04-08 12:49:43 +02:00
|
|
|
print("******************************************\n")
|
2018-04-21 19:23:54 +02:00
|
|
|
diffresult = {}
|
2017-04-08 12:49:43 +02:00
|
|
|
for i in range(1, 2):
|
2020-09-23 23:08:36 +02:00
|
|
|
success = 0
|
2024-05-27 08:50:25 +02:00
|
|
|
for refTableFile in glob.glob("{}/r{}/show_bgp_ipv6*.ref".format(thisDir, i)):
|
2020-09-23 23:08:36 +02:00
|
|
|
if os.path.isfile(refTableFile):
|
|
|
|
# Read expected result from file
|
2025-04-01 08:36:15 +02:00
|
|
|
with open(refTableFile) as file:
|
|
|
|
expected = file.read().rstrip()
|
2020-09-23 23:08:36 +02:00
|
|
|
# Fix newlines (make them all the same)
|
|
|
|
expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
|
|
|
|
|
|
|
|
# Actual output from router
|
|
|
|
actual = (
|
2024-05-27 08:50:25 +02:00
|
|
|
net["r{}".format(i)]
|
|
|
|
.cmd('vtysh -c "show bgp ipv6" 2> /dev/null')
|
|
|
|
.rstrip()
|
2020-10-07 23:22:26 +02:00
|
|
|
)
|
2020-09-23 23:08:36 +02:00
|
|
|
# Remove summary line (changed recently)
|
|
|
|
actual = re.sub(r"Total number.*", "", actual)
|
|
|
|
actual = re.sub(r"Displayed.*", "", actual)
|
|
|
|
actual = actual.rstrip()
|
|
|
|
# Fix newlines (make them all the same)
|
|
|
|
actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
|
|
|
|
|
|
|
|
# Generate Diff
|
|
|
|
diff = topotest.get_textdiff(
|
|
|
|
actual,
|
|
|
|
expected,
|
|
|
|
title1="actual SHOW BGP IPv6",
|
|
|
|
title2="expected SHOW BGP IPv6",
|
|
|
|
)
|
|
|
|
|
|
|
|
# Empty string if it matches, otherwise diff contains unified diff
|
|
|
|
if diff:
|
|
|
|
diffresult[refTableFile] = diff
|
|
|
|
else:
|
|
|
|
success = 1
|
2024-05-27 08:50:25 +02:00
|
|
|
print("template {} matched: r{} ok".format(refTableFile, i))
|
2020-09-23 23:08:36 +02:00
|
|
|
|
|
|
|
if not success:
|
|
|
|
resultstr = "No template matched.\n"
|
2020-11-25 13:49:45 +01:00
|
|
|
for f in diffresult.keys():
|
2024-05-27 08:50:25 +02:00
|
|
|
resultstr += (
|
|
|
|
"template {}: r{} failed SHOW BGP IPv6 check:\n{}\n".format(
|
|
|
|
f,
|
|
|
|
i,
|
|
|
|
diffresult[f],
|
|
|
|
)
|
2020-09-23 23:08:36 +02:00
|
|
|
)
|
|
|
|
raise AssertionError(
|
2024-05-27 08:50:25 +02:00
|
|
|
"SHOW BGP IPv6 failed for router r{}:\n{}".format(i, resultstr)
|
2020-10-07 23:22:26 +02:00
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2017-05-20 05:41:20 +02:00
|
|
|
# Make sure that all daemons are running
|
|
|
|
for i in range(1, 2):
|
2024-05-27 08:50:25 +02:00
|
|
|
fatal_error = net["r{}".format(i)].checkRouterRunning()
|
2017-05-20 05:41:20 +02:00
|
|
|
assert fatal_error == "", fatal_error
|
|
|
|
|
2020-10-07 23:22:26 +02:00
|
|
|
|
2020-04-04 14:14:03 +02:00
|
|
|
def test_route_map():
|
|
|
|
global fatal_error
|
2021-07-29 11:38:55 +02:00
|
|
|
net = get_topogen().net
|
2020-04-04 14:14:03 +02:00
|
|
|
|
|
|
|
if fatal_error != "":
|
|
|
|
pytest.skip(fatal_error)
|
|
|
|
|
|
|
|
thisDir = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
|
|
|
|
print("\n\n** Verifying some basic routemap forward references\n")
|
|
|
|
print("*******************************************************\n")
|
|
|
|
failures = 0
|
|
|
|
for i in range(1, 2):
|
2024-05-27 08:50:25 +02:00
|
|
|
refroutemap = "{}/r{}/show_route_map.ref".format(thisDir, i)
|
2020-04-04 14:14:03 +02:00
|
|
|
if os.path.isfile(refroutemap):
|
2025-04-01 08:36:15 +02:00
|
|
|
with open(refroutemap) as file:
|
|
|
|
expected = file.read().rstrip()
|
2020-04-04 14:14:03 +02:00
|
|
|
expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
|
|
|
|
|
|
|
|
actual = (
|
2024-05-27 08:50:25 +02:00
|
|
|
net["r{}".format(i)]
|
|
|
|
.cmd('vtysh -c "show route-map" 2> /dev/null')
|
|
|
|
.rstrip()
|
2020-10-07 23:22:26 +02:00
|
|
|
)
|
2022-10-10 16:56:51 +02:00
|
|
|
actual = re.sub(r"\([0-9].* milli", "(X milli", actual)
|
2020-04-04 14:14:03 +02:00
|
|
|
actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
|
|
|
|
|
|
|
|
diff = topotest.get_textdiff(
|
|
|
|
actual,
|
|
|
|
expected,
|
|
|
|
title1="actual show route-map",
|
|
|
|
title2="expected show route-map",
|
|
|
|
)
|
|
|
|
|
|
|
|
if diff:
|
|
|
|
sys.stderr.write(
|
2024-05-27 08:50:25 +02:00
|
|
|
"r{} failed show route-map command Check:\n{}\n".format(i, diff)
|
2020-10-07 23:22:26 +02:00
|
|
|
)
|
2020-04-04 14:14:03 +02:00
|
|
|
failures += 1
|
|
|
|
else:
|
2024-05-27 08:50:25 +02:00
|
|
|
print("r{} ok".format(i))
|
2020-10-07 23:22:26 +02:00
|
|
|
|
|
|
|
assert (
|
2017-04-08 12:49:43 +02:00
|
|
|
failures == 0
|
2024-05-27 08:50:25 +02:00
|
|
|
), "Show route-map command failed for router r{}:\n{}".format(i, diff)
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
|
2020-04-14 01:00:18 +02:00
|
|
|
def test_nexthop_groups_with_route_maps():
|
|
|
|
global fatal_error
|
2021-07-29 11:38:55 +02:00
|
|
|
net = get_topogen().net
|
2020-04-14 01:00:18 +02:00
|
|
|
|
|
|
|
# Skip if previous fatal error condition is raised
|
|
|
|
if fatal_error != "":
|
|
|
|
pytest.skip(fatal_error)
|
|
|
|
|
|
|
|
print("\n\n** Verifying Nexthop Groups With Route-Maps")
|
|
|
|
print("******************************************\n")
|
|
|
|
|
|
|
|
### Nexthop Group With Route-Map Tests
|
|
|
|
|
|
|
|
# Create a lib nexthop-group
|
|
|
|
net["r1"].cmd(
|
|
|
|
'vtysh -c "c t" -c "nexthop-group test" -c "nexthop 1.1.1.1" -c "nexthop 1.1.1.2"'
|
|
|
|
)
|
|
|
|
|
|
|
|
## Route-Map Proto Source
|
|
|
|
|
|
|
|
route_str = "2.2.2.1"
|
|
|
|
src_str = "192.168.0.1"
|
|
|
|
|
|
|
|
net["r1"].cmd(
|
2024-05-27 08:50:25 +02:00
|
|
|
'vtysh -c "c t" -c "route-map NH-SRC permit 111" -c "set src {}"'.format(
|
|
|
|
src_str
|
|
|
|
)
|
2020-04-14 01:00:18 +02:00
|
|
|
)
|
|
|
|
net["r1"].cmd('vtysh -c "c t" -c "ip protocol sharp route-map NH-SRC"')
|
|
|
|
|
2024-05-27 08:50:25 +02:00
|
|
|
net["r1"].cmd(
|
|
|
|
'vtysh -c "sharp install routes {} nexthop-group test 1"'.format(route_str)
|
|
|
|
)
|
2020-04-14 01:00:18 +02:00
|
|
|
|
2024-05-27 08:50:25 +02:00
|
|
|
verify_route_nexthop_group("{}/32".format(route_str))
|
2020-04-14 01:00:18 +02:00
|
|
|
|
|
|
|
# Only a valid test on linux using nexthop objects
|
|
|
|
if sys.platform.startswith("linux"):
|
2024-05-27 08:50:25 +02:00
|
|
|
output = net["r1"].cmd("ip route show {}/32".format(route_str))
|
|
|
|
match = re.search(r"src {}".format(src_str), output)
|
|
|
|
assert match is not None, "Route {}/32 not installed with src {}".format(
|
2020-04-14 01:00:18 +02:00
|
|
|
route_str,
|
|
|
|
src_str,
|
|
|
|
)
|
|
|
|
|
|
|
|
# Remove NHG routes and route-map
|
2024-05-27 08:50:25 +02:00
|
|
|
net["r1"].cmd('vtysh -c "sharp remove routes {} 1"'.format(route_str))
|
2020-04-14 01:00:18 +02:00
|
|
|
net["r1"].cmd('vtysh -c "c t" -c "no ip protocol sharp route-map NH-SRC"')
|
|
|
|
net["r1"].cmd(
|
2024-05-27 08:50:25 +02:00
|
|
|
'vtysh -c "c t" -c "no route-map NH-SRC permit 111" # -c "set src {}"'.format(
|
|
|
|
src_str
|
|
|
|
)
|
2020-04-14 01:00:18 +02:00
|
|
|
)
|
|
|
|
net["r1"].cmd('vtysh -c "c t" -c "no route-map NH-SRC"')
|
|
|
|
|
|
|
|
## Route-Map Deny/Permit with same nexthop group
|
|
|
|
|
|
|
|
permit_route_str = "3.3.3.1"
|
|
|
|
deny_route_str = "3.3.3.2"
|
|
|
|
|
|
|
|
net["r1"].cmd(
|
2024-05-27 08:50:25 +02:00
|
|
|
'vtysh -c "c t" -c "ip prefix-list NOPE seq 5 permit {}/32"'.format(
|
|
|
|
permit_route_str
|
|
|
|
)
|
2020-04-14 01:00:18 +02:00
|
|
|
)
|
|
|
|
net["r1"].cmd(
|
|
|
|
'vtysh -c "c t" -c "route-map NOPE permit 111" -c "match ip address prefix-list NOPE"'
|
|
|
|
)
|
|
|
|
net["r1"].cmd('vtysh -c "c t" -c "route-map NOPE deny 222"')
|
|
|
|
net["r1"].cmd('vtysh -c "c t" -c "ip protocol sharp route-map NOPE"')
|
|
|
|
|
|
|
|
# This route should be permitted
|
|
|
|
net["r1"].cmd(
|
2024-05-27 08:50:25 +02:00
|
|
|
'vtysh -c "sharp install routes {} nexthop-group test 1"'.format(
|
|
|
|
permit_route_str
|
|
|
|
)
|
2020-04-14 01:00:18 +02:00
|
|
|
)
|
|
|
|
|
2024-05-27 08:50:25 +02:00
|
|
|
verify_route_nexthop_group("{}/32".format(permit_route_str))
|
2020-04-14 01:00:18 +02:00
|
|
|
|
|
|
|
# This route should be denied
|
|
|
|
net["r1"].cmd(
|
2024-05-27 08:50:25 +02:00
|
|
|
'vtysh -c "sharp install routes {} nexthop-group test 1"'.format(deny_route_str)
|
2020-04-14 01:00:18 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
nhg_id = route_get_nhg_id(deny_route_str)
|
2024-05-27 08:50:25 +02:00
|
|
|
output = net["r1"].cmd('vtysh -c "show nexthop-group rib {}"'.format(nhg_id))
|
2020-04-14 01:00:18 +02:00
|
|
|
|
|
|
|
match = re.search(r"Valid", output)
|
2024-05-27 08:50:25 +02:00
|
|
|
assert match is None, "Nexthop Group ID={} should not be marked Valid".format(
|
|
|
|
nhg_id
|
|
|
|
)
|
2020-04-14 01:00:18 +02:00
|
|
|
|
|
|
|
match = re.search(r"Installed", output)
|
2024-05-27 08:50:25 +02:00
|
|
|
assert match is None, "Nexthop Group ID={} should not be marked Installed".format(
|
|
|
|
nhg_id
|
|
|
|
)
|
2020-04-14 01:00:18 +02:00
|
|
|
|
|
|
|
# Remove NHG routes and route-map
|
2024-05-27 08:50:25 +02:00
|
|
|
net["r1"].cmd('vtysh -c "sharp remove routes {} 1"'.format(permit_route_str))
|
|
|
|
net["r1"].cmd('vtysh -c "sharp remove routes {} 1"'.format(deny_route_str))
|
2020-04-14 01:00:18 +02:00
|
|
|
net["r1"].cmd('vtysh -c "c t" -c "no ip protocol sharp route-map NOPE"')
|
|
|
|
net["r1"].cmd('vtysh -c "c t" -c "no route-map NOPE permit 111"')
|
|
|
|
net["r1"].cmd('vtysh -c "c t" -c "no route-map NOPE deny 222"')
|
|
|
|
net["r1"].cmd('vtysh -c "c t" -c "no route-map NOPE"')
|
|
|
|
net["r1"].cmd(
|
2024-05-27 08:50:25 +02:00
|
|
|
'vtysh -c "c t" -c "no ip prefix-list NOPE seq 5 permit {}/32"'.format(
|
|
|
|
permit_route_str
|
|
|
|
)
|
2020-04-14 01:00:18 +02:00
|
|
|
)
|
2020-10-07 23:22:26 +02:00
|
|
|
|
2020-04-14 01:00:18 +02:00
|
|
|
|
2020-08-03 20:43:01 +02:00
|
|
|
def test_nexthop_group_replace():
|
|
|
|
global fatal_error
|
2021-07-29 11:38:55 +02:00
|
|
|
net = get_topogen().net
|
2020-08-03 20:43:01 +02:00
|
|
|
|
|
|
|
# Skip if previous fatal error condition is raised
|
|
|
|
if fatal_error != "":
|
|
|
|
pytest.skip(fatal_error)
|
|
|
|
|
|
|
|
print("\n\n** Verifying Nexthop Groups")
|
|
|
|
print("******************************************\n")
|
|
|
|
|
|
|
|
### Nexthop Group Tests
|
|
|
|
|
|
|
|
## 2-Way ECMP Directly Connected
|
|
|
|
|
|
|
|
net["r1"].cmd(
|
|
|
|
'vtysh -c "c t" -c "nexthop-group replace" -c "nexthop 1.1.1.1 r1-eth1 onlink" -c "nexthop 1.1.1.2 r1-eth2 onlink"'
|
|
|
|
)
|
|
|
|
|
2021-11-04 16:45:27 +01:00
|
|
|
# At the moment there is absolutely no real easy way to query sharpd
|
|
|
|
# for the nexthop group actually installed. If it is not installed
|
|
|
|
# sharpd will just transmit the nexthops down instead of the nexthop
|
|
|
|
# group id. Leading to a situation where the replace is not actually
|
|
|
|
# being tested. So let's just wait some time here because this
|
|
|
|
# is hard and this test fails all the time
|
|
|
|
sleep(5)
|
|
|
|
|
2020-08-03 20:43:01 +02:00
|
|
|
# Create with sharpd using nexthop-group
|
|
|
|
net["r1"].cmd('vtysh -c "sharp install routes 3.3.3.1 nexthop-group replace 1"')
|
|
|
|
|
|
|
|
verify_route_nexthop_group("3.3.3.1/32")
|
|
|
|
|
|
|
|
# Change the nexthop group
|
|
|
|
net["r1"].cmd(
|
|
|
|
'vtysh -c "c t" -c "nexthop-group replace" -c "no nexthop 1.1.1.1 r1-eth1 onlink" -c "nexthop 1.1.1.3 r1-eth1 onlink" -c "nexthop 1.1.1.4 r1-eth4 onlink"'
|
|
|
|
)
|
|
|
|
|
|
|
|
# Verify it updated. We can just check install and ecmp count here.
|
|
|
|
verify_route_nexthop_group("3.3.3.1/32", False, 3)
|
|
|
|
|
2020-10-07 23:22:26 +02:00
|
|
|
|
2017-04-08 12:49:43 +02:00
|
|
|
def test_mpls_interfaces():
|
|
|
|
global fatal_error
|
2021-07-29 11:38:55 +02:00
|
|
|
net = get_topogen().net
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
# Skip if previous fatal error condition is raised
|
|
|
|
if fatal_error != "":
|
|
|
|
pytest.skip(fatal_error)
|
|
|
|
|
|
|
|
# Skip if no LDP installed or old kernel
|
|
|
|
if net["r1"].daemon_available("ldpd") == False:
|
|
|
|
pytest.skip("No MPLS or kernel < 4.5")
|
|
|
|
|
|
|
|
thisDir = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
|
2017-04-26 18:18:58 +02:00
|
|
|
print("\n\n** Verifying MPLS Interfaces")
|
2017-04-08 12:49:43 +02:00
|
|
|
print("******************************************\n")
|
|
|
|
failures = 0
|
|
|
|
for i in range(1, 2):
|
2024-05-27 08:50:25 +02:00
|
|
|
refTableFile = "{}/r{}/show_mpls_ldp_interface.ref".format(thisDir, i)
|
2017-04-08 12:49:43 +02:00
|
|
|
if os.path.isfile(refTableFile):
|
|
|
|
# Read expected result from file
|
2025-04-01 08:36:15 +02:00
|
|
|
with open(refTableFile) as file:
|
|
|
|
expected = file.read().rstrip()
|
2017-04-08 12:49:43 +02:00
|
|
|
# Fix newlines (make them all the same)
|
|
|
|
expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
|
|
|
|
|
|
|
|
# Actual output from router
|
|
|
|
actual = (
|
2024-05-27 08:50:25 +02:00
|
|
|
net["r{}".format(i)]
|
2017-04-08 12:49:43 +02:00
|
|
|
.cmd('vtysh -c "show mpls ldp interface" 2> /dev/null')
|
|
|
|
.rstrip()
|
2020-10-07 23:22:26 +02:00
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
# Mask out Timer in Uptime
|
|
|
|
actual = re.sub(r" [0-9][0-9]:[0-9][0-9]:[0-9][0-9] ", " xx:xx:xx ", actual)
|
|
|
|
# Fix newlines (make them all the same)
|
|
|
|
actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
|
|
|
|
|
|
|
|
# Generate Diff
|
2017-05-31 04:39:21 +02:00
|
|
|
diff = topotest.get_textdiff(
|
|
|
|
actual,
|
|
|
|
expected,
|
|
|
|
title1="actual MPLS LDP interface status",
|
|
|
|
title2="expected MPLS LDP interface status",
|
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
# Empty string if it matches, otherwise diff contains unified diff
|
|
|
|
if diff:
|
|
|
|
sys.stderr.write(
|
2024-05-27 08:50:25 +02:00
|
|
|
"r{} failed MPLS LDP Interface status Check:\n{}\n".format(i, diff)
|
2020-10-07 23:22:26 +02:00
|
|
|
)
|
2017-04-08 12:49:43 +02:00
|
|
|
failures += 1
|
|
|
|
else:
|
2024-05-27 08:50:25 +02:00
|
|
|
print("r{} ok".format(i))
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
if failures > 0:
|
|
|
|
fatal_error = "MPLS LDP Interface status failed"
|
|
|
|
|
|
|
|
assert (
|
|
|
|
failures == 0
|
2024-05-27 08:50:25 +02:00
|
|
|
), "MPLS LDP Interface status failed for router r{}:\n{}".format(i, diff)
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2017-05-20 05:41:20 +02:00
|
|
|
# Make sure that all daemons are running
|
|
|
|
for i in range(1, 2):
|
2024-05-27 08:50:25 +02:00
|
|
|
fatal_error = net["r{}".format(i)].checkRouterRunning()
|
2017-05-20 05:41:20 +02:00
|
|
|
assert fatal_error == "", fatal_error
|
|
|
|
|
2017-04-08 12:49:43 +02:00
|
|
|
|
2022-10-26 21:47:08 +02:00
|
|
|
def test_resilient_nexthop_group():
|
2024-08-14 14:37:00 +02:00
|
|
|
global fatal_error
|
2022-10-26 21:47:08 +02:00
|
|
|
net = get_topogen().net
|
|
|
|
|
2024-08-14 14:37:00 +02:00
|
|
|
# Skip if previous fatal error condition is raised
|
|
|
|
if fatal_error != "":
|
|
|
|
pytest.skip(fatal_error)
|
|
|
|
|
2022-10-26 21:47:08 +02:00
|
|
|
result = required_linux_kernel_version("5.19")
|
|
|
|
if result is not True:
|
|
|
|
pytest.skip("Kernel requirements are not met, kernel version should be >= 5.19")
|
|
|
|
|
|
|
|
net["r1"].cmd(
|
|
|
|
'vtysh -c "conf" -c "nexthop-group resilience" -c "resilient buckets 64 idle-timer 128 unbalanced-timer 256" -c "nexthop 1.1.1.1 r1-eth1 onlink" -c "nexthop 1.1.1.2 r1-eth2 onlink"'
|
|
|
|
)
|
|
|
|
|
2024-08-14 14:37:00 +02:00
|
|
|
# Temporary helper function
|
|
|
|
def _show_func():
|
|
|
|
output = net["r1"].cmd('vtysh -c "show nexthop-group rib sharp"')
|
|
|
|
buckets = re.findall(r"Buckets", output)
|
|
|
|
|
|
|
|
return len(buckets)
|
|
|
|
|
|
|
|
_, result = topotest.run_and_expect(_show_func, 1, count=30, wait=1)
|
|
|
|
if result != 1:
|
|
|
|
fatal_error = "Resilient NHG not created in zebra"
|
|
|
|
|
|
|
|
assert result == 1, fatal_error
|
2023-10-05 19:47:17 +02:00
|
|
|
|
|
|
|
output = net["r1"].cmd('vtysh -c "show nexthop-group rib sharp json"')
|
|
|
|
|
|
|
|
joutput = json.loads(output)
|
|
|
|
|
|
|
|
# Use the json output and collect the nhg id from it
|
|
|
|
|
|
|
|
for nhgid in joutput:
|
|
|
|
n = joutput[nhgid]
|
|
|
|
if "buckets" in n:
|
|
|
|
break
|
2022-10-26 21:47:08 +02:00
|
|
|
|
2024-08-14 14:37:00 +02:00
|
|
|
if "buckets" not in n:
|
|
|
|
fatal_error = "Resilient NHG not found in json output"
|
|
|
|
assert "buckets" in n, fatal_error
|
|
|
|
|
2023-10-05 19:47:17 +02:00
|
|
|
verify_nexthop_group(int(nhgid))
|
2024-08-14 14:37:00 +02:00
|
|
|
|
|
|
|
# Remove NHG
|
|
|
|
net["r1"].cmd('vtysh -c "conf" -c "no nexthop-group resilience"')
|
2022-10-26 21:47:08 +02:00
|
|
|
|
|
|
|
|
2024-09-20 03:06:40 +02:00
|
|
|
def test_interface_stuff():
|
|
|
|
global fatal_error
|
|
|
|
net = get_topogen().net
|
|
|
|
|
|
|
|
# Skip if previous fatal error condition is raised
|
|
|
|
if fatal_error != "":
|
|
|
|
pytest.skip(fatal_error)
|
|
|
|
|
|
|
|
print("\n\n** Verifying some interface code")
|
|
|
|
print("************************************\n")
|
|
|
|
|
|
|
|
net["r1"].cmd('vtysh -c "conf" -c "interface r1-eth0" -c "multicast enable"')
|
|
|
|
|
|
|
|
def _test_interface_multicast_on():
|
|
|
|
output = json.loads(net["r1"].cmd('vtysh -c "show int r1-eth0 json"'))
|
|
|
|
expected = {
|
|
|
|
"r1-eth0": {
|
|
|
|
"flags": "<UP,LOWER_UP,BROADCAST,RUNNING,MULTICAST>",
|
|
|
|
"multicastConfig": "Enabled by CLI",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return topotest.json_cmp(output, expected)
|
|
|
|
|
|
|
|
test_func = functools.partial(_test_interface_multicast_on)
|
|
|
|
_, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
|
|
|
|
assert result is None, "Multicast bit was not set on r1-eth0"
|
|
|
|
|
|
|
|
net["r1"].cmd('vtysh -c "conf" -c "interface r1-eth0" -c "multicast disable"')
|
|
|
|
|
|
|
|
def _test_interface_multicast_off():
|
|
|
|
output = json.loads(
|
|
|
|
net["r1"].cmd('vtysh -c "show int r1-eth0 vrf default json"')
|
|
|
|
)
|
|
|
|
expected = {
|
|
|
|
"r1-eth0": {
|
|
|
|
"flags": "<UP,LOWER_UP,BROADCAST,RUNNING>",
|
|
|
|
"multicastConfig": "Disabled by CLI",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return topotest.json_cmp(output, expected)
|
|
|
|
|
|
|
|
test_func = functools.partial(_test_interface_multicast_off)
|
|
|
|
_, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
|
|
|
|
assert result is None, "Multicast bit was not turned off on r1-eth0"
|
|
|
|
|
|
|
|
net["r1"].cmd('vtysh -c "conf" -c "interface r1-eth0" -c "no multicast disable"')
|
|
|
|
|
|
|
|
def _test_interface_multicast_disable():
|
|
|
|
output = json.loads(net["r1"].cmd('vtysh -c "show int r1-eth0 json"'))
|
|
|
|
expected = {
|
|
|
|
"r1-eth0": {
|
|
|
|
"flags": "<UP,LOWER_UP,BROADCAST,RUNNING>",
|
|
|
|
"multicastConfig": "Not specified by CLI",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return topotest.json_cmp(output, expected)
|
|
|
|
|
|
|
|
test_func = functools.partial(_test_interface_multicast_disable)
|
|
|
|
_, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
|
|
|
|
assert result is None, "Multicast bit was set on r1-eth0"
|
|
|
|
|
|
|
|
logger.info("Ensure that these commands are still nominally working")
|
|
|
|
rc, o, e = net["r1"].cmd_status('vtysh -c "show interface description vrf all"')
|
|
|
|
logger.info(o)
|
|
|
|
assert rc == 0
|
|
|
|
|
|
|
|
rc, o, e = net["r1"].cmd_status('vtysh -c "show interface description vrf default"')
|
|
|
|
logger.info(o)
|
|
|
|
assert rc == 0
|
|
|
|
|
|
|
|
|
2017-04-08 12:49:43 +02:00
|
|
|
def test_shutdown_check_stderr():
|
|
|
|
global fatal_error
|
2021-07-29 11:38:55 +02:00
|
|
|
net = get_topogen().net
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
# Skip if previous fatal error condition is raised
|
|
|
|
if fatal_error != "":
|
|
|
|
pytest.skip(fatal_error)
|
|
|
|
|
2017-04-26 18:18:58 +02:00
|
|
|
print("\n\n** Verifying unexpected STDERR output from daemons")
|
2017-04-08 12:49:43 +02:00
|
|
|
print("******************************************\n")
|
|
|
|
|
|
|
|
if os.environ.get("TOPOTESTS_CHECK_STDERR") is None:
|
2017-04-27 04:54:25 +02:00
|
|
|
print(
|
|
|
|
"SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n"
|
|
|
|
)
|
|
|
|
pytest.skip("Skipping test for Stderr output")
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
thisDir = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
|
2017-04-27 04:54:25 +02:00
|
|
|
print("thisDir=" + thisDir)
|
|
|
|
|
2017-04-08 12:49:43 +02:00
|
|
|
net["r1"].stopRouter()
|
|
|
|
|
|
|
|
log = net["r1"].getStdErr("ripd")
|
2017-08-31 02:03:54 +02:00
|
|
|
if log:
|
|
|
|
print("\nRIPd StdErr Log:\n" + log)
|
2017-04-08 12:49:43 +02:00
|
|
|
log = net["r1"].getStdErr("ripngd")
|
2017-08-31 02:03:54 +02:00
|
|
|
if log:
|
|
|
|
print("\nRIPngd StdErr Log:\n" + log)
|
2017-04-08 12:49:43 +02:00
|
|
|
log = net["r1"].getStdErr("ospfd")
|
2017-08-31 02:03:54 +02:00
|
|
|
if log:
|
|
|
|
print("\nOSPFd StdErr Log:\n" + log)
|
2017-04-08 12:49:43 +02:00
|
|
|
log = net["r1"].getStdErr("ospf6d")
|
2017-08-31 02:03:54 +02:00
|
|
|
if log:
|
|
|
|
print("\nOSPF6d StdErr Log:\n" + log)
|
2017-04-08 12:49:43 +02:00
|
|
|
log = net["r1"].getStdErr("isisd")
|
2017-08-31 02:03:54 +02:00
|
|
|
if log:
|
|
|
|
print("\nISISd StdErr Log:\n" + log)
|
2017-04-08 12:49:43 +02:00
|
|
|
log = net["r1"].getStdErr("bgpd")
|
2017-08-31 02:03:54 +02:00
|
|
|
if log:
|
|
|
|
print("\nBGPd StdErr Log:\n" + log)
|
2020-08-25 16:50:12 +02:00
|
|
|
|
|
|
|
log = net["r1"].getStdErr("nhrpd")
|
|
|
|
if log:
|
|
|
|
print("\nNHRPd StdErr Log:\n" + log)
|
|
|
|
|
|
|
|
log = net["r1"].getStdErr("pbrd")
|
|
|
|
if log:
|
|
|
|
print("\nPBRd StdErr Log:\n" + log)
|
|
|
|
|
|
|
|
log = net["r1"].getStdErr("babeld")
|
|
|
|
if log:
|
|
|
|
print("\nBABELd StdErr Log:\n" + log)
|
|
|
|
|
2017-04-08 12:49:43 +02:00
|
|
|
if net["r1"].daemon_available("ldpd"):
|
|
|
|
log = net["r1"].getStdErr("ldpd")
|
2017-08-31 02:03:54 +02:00
|
|
|
if log:
|
|
|
|
print("\nLDPd StdErr Log:\n" + log)
|
2017-04-08 12:49:43 +02:00
|
|
|
log = net["r1"].getStdErr("zebra")
|
2017-08-31 02:03:54 +02:00
|
|
|
if log:
|
|
|
|
print("\nZebra StdErr Log:\n" + log)
|
2017-04-08 12:49:43 +02:00
|
|
|
|
|
|
|
|
2017-04-27 04:54:25 +02:00
|
|
|
def test_shutdown_check_memleak():
|
|
|
|
global fatal_error
|
2021-07-29 11:38:55 +02:00
|
|
|
net = get_topogen().net
|
2017-04-27 04:54:25 +02:00
|
|
|
|
|
|
|
# Skip if previous fatal error condition is raised
|
|
|
|
if fatal_error != "":
|
|
|
|
pytest.skip(fatal_error)
|
|
|
|
|
|
|
|
if os.environ.get("TOPOTESTS_CHECK_MEMLEAK") is None:
|
|
|
|
print(
|
|
|
|
"SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n"
|
|
|
|
)
|
|
|
|
pytest.skip("Skipping test for memory leaks")
|
2020-10-07 23:22:26 +02:00
|
|
|
|
2017-04-27 04:54:25 +02:00
|
|
|
thisDir = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
|
|
|
|
for i in range(1, 2):
|
2024-05-27 08:50:25 +02:00
|
|
|
net["r{}".format(i)].stopRouter()
|
|
|
|
net["r{}".format(i)].report_memory_leaks(
|
2017-04-27 04:54:25 +02:00
|
|
|
os.environ.get("TOPOTESTS_CHECK_MEMLEAK"), os.path.basename(__file__)
|
2020-10-07 23:22:26 +02:00
|
|
|
)
|
2017-04-27 04:54:25 +02:00
|
|
|
|
|
|
|
|
2017-04-08 12:49:43 +02:00
|
|
|
if __name__ == "__main__":
|
|
|
|
# To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli
|
|
|
|
# retval = pytest.main(["-s", "--tb=no"])
|
|
|
|
retval = pytest.main(["-s"])
|
|
|
|
sys.exit(retval)
|