384 lines
13 KiB
Python
384 lines
13 KiB
Python
# -*- coding: utf-8 eval: (blacken-mode 1) -*-
|
|
# SPDX-License-Identifier: ISC
|
|
#
|
|
# June 10 2023, Christian Hopps <chopps@labn.net>
|
|
#
|
|
# Copyright (c) 2023, LabN Consulting, L.L.C.
|
|
#
|
|
"""
|
|
Test mgmtd parsing of configs.
|
|
|
|
So:
|
|
|
|
MGMTD matches zebra:
|
|
|
|
one exit file: ONE: vty -f file
|
|
one exit redir: ONE: vty < file
|
|
early exit file: ONE: vty -f file
|
|
early exit redir: ONE: vty < file
|
|
early end file: ALL: vty -f file
|
|
early end redir: ONE: vty < file
|
|
|
|
Raw tests:
|
|
|
|
FAILED mgmt_config/test_config.py::test_mgmtd_one_exit_file - AssertionError: vtysh < didn't work after exit
|
|
FAILED mgmt_config/test_config.py::test_mgmtd_one_exit_redir - AssertionError: vtysh < didn't work after exit
|
|
FAILED mgmt_config/test_config.py::test_mgmtd_early_exit_file - AssertionError: vtysh -f didn't work after 1 exit
|
|
FAILED mgmt_config/test_config.py::test_mgmtd_early_exit_redir - AssertionError: vtysh < didn't work after 1 exits
|
|
FAILED mgmt_config/test_config.py::test_mgmtd_early_end_redir - AssertionError: vtysh < didn't work after 1 end
|
|
|
|
FAILED mgmt_config/test_config.py::test_zebra_one_exit_file - AssertionError: zebra second conf missing
|
|
FAILED mgmt_config/test_config.py::test_zebra_one_exit_redir - AssertionError: zebra second conf missing
|
|
FAILED mgmt_config/test_config.py::test_zebra_early_exit_file - AssertionError: zebra second conf missing
|
|
FAILED mgmt_config/test_config.py::test_zebra_early_exit_redir - AssertionError: zebra second conf missing
|
|
FAILED mgmt_config/test_config.py::test_zebra_early_end_redir - AssertionError: zebra second conf missing
|
|
|
|
Before fixed:
|
|
|
|
one exit file: NONE: vty -f file
|
|
early exit file: NONE: vty -f file
|
|
|
|
FAILED mgmt_config/test_config.py::test_mgmtd_one_exit_file - AssertionError: vtysh -f didn't work before exit
|
|
FAILED mgmt_config/test_config.py::test_mgmtd_one_exit_redir - AssertionError: vtysh < didn't work after exit
|
|
FAILED mgmt_config/test_config.py::test_mgmtd_early_exit_file - AssertionError: vtysh -f didn't work before exit
|
|
FAILED mgmt_config/test_config.py::test_mgmtd_early_exit_redir - AssertionError: vtysh < didn't work after 1 exits
|
|
FAILED mgmt_config/test_config.py::test_mgmtd_early_end_redir - AssertionError: vtysh < didn't work after 1 end
|
|
|
|
FAILED mgmt_config/test_config.py::test_zebra_one_exit_file - AssertionError: zebra second conf missing
|
|
FAILED mgmt_config/test_config.py::test_zebra_one_exit_redir - AssertionError: zebra second conf missing
|
|
FAILED mgmt_config/test_config.py::test_zebra_early_exit_file - AssertionError: zebra second conf missing
|
|
FAILED mgmt_config/test_config.py::test_zebra_early_exit_redir - AssertionError: zebra second conf missing
|
|
FAILED mgmt_config/test_config.py::test_zebra_early_end_redir - AssertionError: zebra second conf missing
|
|
|
|
"""
|
|
import ipaddress
|
|
import logging
|
|
import os
|
|
import re
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from lib.common_config import retry, step
|
|
from lib.topogen import Topogen, TopoRouter
|
|
|
|
pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd]
|
|
|
|
|
|
@retry(retry_timeout=1, initial_wait=0.1)
|
|
def check_kernel(r1, prefix, expected=True):
|
|
net = ipaddress.ip_network(prefix)
|
|
if net.version == 6:
|
|
kernel = r1.cmd_nostatus("ip -6 route show", warn=not expected)
|
|
else:
|
|
kernel = r1.cmd_nostatus("ip -4 route show", warn=not expected)
|
|
|
|
logging.debug("checking kernel routing table:\n%0.1920s", kernel)
|
|
route = f"{str(net)}(?: nhid [0-9]+)?.*proto (static|196)"
|
|
m = re.search(route, kernel)
|
|
if expected and not m:
|
|
return f"Failed to find \n'{route}'\n in \n'{kernel:.1920}'"
|
|
elif not expected and m:
|
|
return f"Failed found \n'{route}'\n in \n'{kernel:.1920}'"
|
|
return None
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def tgen(request):
|
|
"Setup/Teardown the environment and provide tgen argument to tests"
|
|
|
|
topodef = {"s1": ("r1",)}
|
|
|
|
tgen = Topogen(topodef, request.module.__name__)
|
|
tgen.start_topology()
|
|
|
|
# configure mgmtd using current mgmtd config file
|
|
tgen.gears["r1"].load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
|
|
tgen.gears["r1"].load_config(TopoRouter.RD_MGMTD)
|
|
|
|
tgen.start_router()
|
|
yield tgen
|
|
tgen.stop_topology()
|
|
|
|
|
|
def save_log_snippet(logfile, content, savepath=None):
|
|
os.sync()
|
|
os.sync()
|
|
os.sync()
|
|
|
|
with open(logfile, encoding="utf-8") as f:
|
|
buf = f.read()
|
|
assert content == buf[: len(content)]
|
|
newcontent = buf[len(content) :]
|
|
|
|
if savepath:
|
|
with open(savepath, "w", encoding="utf-8") as f:
|
|
f.write(newcontent)
|
|
|
|
return buf
|
|
|
|
|
|
def mapname(lname):
|
|
return lname.replace(".conf", "") + "-log.txt"
|
|
|
|
|
|
logbuf = ""
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def r1(tgen):
|
|
return tgen.gears["r1"].net
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def confdir():
|
|
return Path(os.environ["PYTEST_TOPOTEST_SCRIPTDIR"]) / "r1"
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def tempdir(r1):
|
|
return Path(r1.rundir)
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def logpath(tempdir):
|
|
return tempdir / "mgmtd.log"
|
|
|
|
|
|
@pytest.fixture(autouse=True, scope="function")
|
|
def cleanup_config(r1, tempdir, logpath):
|
|
global logbuf
|
|
|
|
logbuf = save_log_snippet(logpath, logbuf, "/dev/null")
|
|
|
|
yield
|
|
|
|
r1.cmd_nostatus("vtysh -c 'conf t' -c 'no allow-external-route-update'")
|
|
r1.cmd_nostatus("vtysh -c 'conf t' -c 'no ip multicast rpf-lookup-mode urib-only'")
|
|
r1.cmd_nostatus("vtysh -c 'conf t' -c 'no ip table range 2 3'")
|
|
|
|
logbuf = save_log_snippet(logpath, logbuf, "/dev/null")
|
|
|
|
|
|
def test_staticd_startup(r1):
|
|
r1.cmd_nostatus(
|
|
"vtysh -c 'debug mgmt client frontend' "
|
|
"-c 'debug mgmt client backend' "
|
|
"-c 'debug mgmt backend frontend datastore transaction'"
|
|
)
|
|
step("Verifying routes are present on r1")
|
|
result = check_kernel(r1, "12.0.0.0/24", retry_timeout=3.0)
|
|
assert result is None
|
|
|
|
|
|
def test_mgmtd_one_exit_file(r1, confdir, tempdir, logpath):
|
|
global logbuf
|
|
|
|
conf = "one-exit.conf"
|
|
step(f"load {conf} file with vtysh -f ")
|
|
output = r1.cmd_nostatus(f"vtysh -f {confdir / conf}")
|
|
logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf))
|
|
print(output)
|
|
|
|
result1 = check_kernel(r1, "20.1.0.0/24")
|
|
result2 = check_kernel(r1, "20.2.0.0/24")
|
|
|
|
assert result1 is None, "vtysh -f didn't work before exit"
|
|
assert result2 is not None, "vtysh < worked after exit, unexpected"
|
|
|
|
|
|
def test_mgmtd_one_exit_redir(r1, confdir, tempdir, logpath):
|
|
global logbuf
|
|
|
|
conf = "one-exit2.conf"
|
|
step(f"Redirect {conf} file into vtysh")
|
|
output = r1.cmd_nostatus(f"vtysh < {confdir / conf}")
|
|
logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf))
|
|
print(output)
|
|
|
|
result1 = check_kernel(r1, "21.1.0.0/24")
|
|
result2 = check_kernel(r1, "21.2.0.0/24")
|
|
|
|
assert result1 is None, "vtysh < didn't work before exit"
|
|
assert result2 is not None, "vtysh < worked after exit, unexpected"
|
|
|
|
|
|
def test_mgmtd_early_exit_file(r1, confdir, tempdir, logpath):
|
|
global logbuf
|
|
|
|
conf = "early-exit.conf"
|
|
step(f"load {conf} file with vtysh -f ")
|
|
output = r1.cmd_nostatus(f"vtysh -f {confdir / conf}")
|
|
logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf))
|
|
print(output)
|
|
|
|
result1 = check_kernel(r1, "13.1.0.0/24")
|
|
result2 = check_kernel(r1, "13.2.0.0/24")
|
|
result3 = check_kernel(r1, "13.3.0.0/24")
|
|
|
|
assert result1 is None, "vtysh -f didn't work before exit"
|
|
assert result2 is not None, "vtysh -f worked after 1 exit, unexpected"
|
|
assert result3 is not None, "vtysh -f worked after 2 exit, unexpected"
|
|
|
|
|
|
def test_mgmtd_early_exit_redir(r1, confdir, tempdir, logpath):
|
|
global logbuf
|
|
|
|
conf = "early-exit2.conf"
|
|
step(f"Redirect {conf} file into vtysh")
|
|
output = r1.cmd_nostatus(f"vtysh < {confdir / conf}")
|
|
logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf))
|
|
print(output)
|
|
|
|
result1 = check_kernel(r1, "14.1.0.0/24")
|
|
result2 = check_kernel(r1, "14.2.0.0/24")
|
|
result3 = check_kernel(r1, "14.3.0.0/24")
|
|
|
|
assert result1 is None, "vtysh < didn't work before exit"
|
|
assert result2 is not None, "vtysh < worked after 1 exits, unexpected"
|
|
assert result3 is not None, "vtysh < worked after 2 exits, unexpected"
|
|
|
|
|
|
def test_mgmtd_early_end_file(r1, confdir, tempdir, logpath):
|
|
global logbuf
|
|
|
|
conf = "early-end.conf"
|
|
step(f"load {conf} file with vtysh -f ")
|
|
output = r1.cmd_nostatus(f"vtysh -f {confdir / conf}")
|
|
logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf))
|
|
print(output)
|
|
|
|
result1 = check_kernel(r1, "15.1.0.0/24")
|
|
result2 = check_kernel(r1, "15.2.0.0/24")
|
|
result3 = check_kernel(r1, "15.3.0.0/24")
|
|
|
|
assert result1 is None, "vtysh -f didn't work before end"
|
|
assert result2 is None, "vtysh -f didn't work after 1 end"
|
|
assert result3 is None, "vtysh -f didn't work after 2 ends"
|
|
|
|
|
|
def test_mgmtd_early_end_redir(r1, confdir, tempdir, logpath):
|
|
global logbuf
|
|
|
|
conf = "early-end2.conf"
|
|
step(f"Redirect {conf} file into vtysh")
|
|
output = r1.cmd_nostatus(f"vtysh < {confdir / conf}")
|
|
logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf))
|
|
print(output)
|
|
|
|
result1 = check_kernel(r1, "16.1.0.0/24")
|
|
result2 = check_kernel(r1, "16.2.0.0/24")
|
|
result3 = check_kernel(r1, "16.3.0.0/24")
|
|
|
|
assert result1 is None, "vtysh < didn't work before end"
|
|
assert result2 is not None, "vtysh < worked after 1 end, unexpected"
|
|
assert result3 is not None, "vtysh < worked after 2 end, unexpected"
|
|
|
|
|
|
#
|
|
# Zebra
|
|
#
|
|
|
|
|
|
def test_zebra_one_exit_file(r1, confdir, tempdir, logpath):
|
|
global logbuf
|
|
|
|
conf = "one-exit-zebra.conf"
|
|
step(f"load {conf} file with vtysh -f ")
|
|
output = r1.cmd_nostatus(f"vtysh -f {confdir / conf}")
|
|
logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf))
|
|
print(output)
|
|
|
|
showrun = r1.cmd_nostatus("vtysh -c 'show running'")
|
|
assert "allow-external-route-update" in showrun, "zebra conf missing"
|
|
assert (
|
|
"ip multicast rpf-lookup-mode urib-only" not in showrun
|
|
), "zebra second conf present, unexpected"
|
|
|
|
|
|
def test_zebra_one_exit_redir(r1, confdir, tempdir, logpath):
|
|
global logbuf
|
|
|
|
conf = "one-exit2-zebra.conf"
|
|
step(f"Redirect {conf} file into vtysh")
|
|
output = r1.cmd_nostatus(f"vtysh < {confdir / conf}")
|
|
logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf))
|
|
print(output)
|
|
|
|
showrun = r1.cmd_nostatus("vtysh -c 'show running'")
|
|
|
|
assert "allow-external-route-update" in showrun, "zebra conf missing"
|
|
assert (
|
|
"ip multicast rpf-lookup-mode urib-only" not in showrun
|
|
), "zebra second conf present, unexpected"
|
|
|
|
|
|
def test_zebra_early_exit_file(r1, confdir, tempdir, logpath):
|
|
global logbuf
|
|
|
|
conf = "early-exit-zebra.conf"
|
|
step(f"load {conf} file with vtysh -f ")
|
|
output = r1.cmd_nostatus(f"vtysh -f {confdir / conf}")
|
|
logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf))
|
|
print(output)
|
|
|
|
showrun = r1.cmd_nostatus("vtysh -c 'show running'")
|
|
|
|
assert "allow-external-route-update" in showrun, "zebra conf missing"
|
|
assert (
|
|
"ip multicast rpf-lookup-mode urib-only" not in showrun
|
|
), "zebra second conf present, unexpected"
|
|
assert "ip table range 2 3" not in showrun, "zebra third conf present, unexpected"
|
|
|
|
|
|
def test_zebra_early_exit_redir(r1, confdir, tempdir, logpath):
|
|
global logbuf
|
|
|
|
conf = "early-exit2-zebra.conf"
|
|
step(f"Redirect {conf} file into vtysh")
|
|
output = r1.cmd_nostatus(f"vtysh < {confdir / conf}")
|
|
logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf))
|
|
print(output)
|
|
|
|
showrun = r1.cmd_nostatus("vtysh -c 'show running'")
|
|
|
|
assert "allow-external-route-update" in showrun, "zebra conf missing"
|
|
assert (
|
|
"ip multicast rpf-lookup-mode urib-only" not in showrun
|
|
), "zebra second conf present, unexpected"
|
|
assert "ip table range 2 3" not in showrun, "zebra third conf present, unexpected"
|
|
|
|
|
|
def test_zebra_early_end_file(r1, confdir, tempdir, logpath):
|
|
global logbuf
|
|
|
|
conf = "early-end-zebra.conf"
|
|
step(f"load {conf} file with vtysh -f ")
|
|
output = r1.cmd_nostatus(f"vtysh -f {confdir / conf}")
|
|
logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf))
|
|
print(output)
|
|
|
|
showrun = r1.cmd_nostatus("vtysh -c 'show running'")
|
|
|
|
assert "allow-external-route-update" in showrun, "zebra conf missing"
|
|
assert (
|
|
"ip multicast rpf-lookup-mode urib-only" in showrun
|
|
), "zebra second conf missing"
|
|
assert "ip table range 2 3" in showrun, "zebra third missing"
|
|
|
|
|
|
def test_zebra_early_end_redir(r1, confdir, tempdir, logpath):
|
|
global logbuf
|
|
|
|
conf = "early-end2-zebra.conf"
|
|
step(f"Redirect {conf} file into vtysh")
|
|
output = r1.cmd_nostatus(f"vtysh < {confdir / conf}")
|
|
logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf))
|
|
print(output)
|
|
|
|
showrun = r1.cmd_nostatus("vtysh -c 'show running'")
|
|
|
|
assert "allow-external-route-update" in showrun, "zebra conf missing"
|
|
assert (
|
|
"ip multicast rpf-lookup-mode urib-only" not in showrun
|
|
), "zebra second conf present, unexpected"
|
|
assert "ip table range 2 3" not in showrun, "zebra third conf present, unexpected"
|