mirror_frr/tests/topotests/ospfapi/test_ospf_clientapi.py
Acee 4e7eb1e62c ospfd: OSPF opaque LSA stale processing fix and topotests.
1. Fix OSPF opaque LSA processing to preserve the stale opaque
            LSAs in the Link State Database for 60 seconds consistent with
            what is done for other LSA types.
         2. Add a topotest that tests for cases where ospfd is restarted
            and a stale OSPF opaque LSA exists in the OSPF routing domain
            both when the LSA is purged and when the LSA is reoriginagted
            with a more recent instance.

Signed-off-by: Acee <aceelindem@gmail.com>
2023-05-09 16:51:03 -04:00

1135 lines
37 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 eval: (blacken-mode 1) -*-
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Copyright (c) 2021-2022, LabN Consulting, L.L.C.
#
"""
test_ospf_clientapi.py: Test the OSPF client API.
"""
import logging
import os
import re
import signal
import subprocess
import sys
import time
from datetime import datetime, timedelta
import pytest
from lib.common_config import (
retry,
run_frr_cmd,
step,
kill_router_daemons,
start_router_daemons,
shutdown_bringup_interface,
)
from lib.micronet import Timeout, comm_error
from lib.topogen import Topogen, TopoRouter
from lib.topotest import interface_set_status, json_cmp
pytestmark = [pytest.mark.ospfd]
CWD = os.path.dirname(os.path.realpath(__file__))
TESTDIR = os.path.abspath(CWD)
CLIENTDIR = os.path.abspath(os.path.join(CWD, "../../../ospfclient"))
if not os.path.exists(CLIENTDIR):
CLIENTDIR = os.path.join(CWD, "/usr/lib/frr")
assert os.path.exists(
os.path.join(CLIENTDIR, "ospfclient.py")
), "can't locate ospfclient.py"
# ----------
# Test Setup
# ----------
#
# r1 - r2
# | |
# r4 - r3
#
@pytest.fixture(scope="function", name="tgen")
def _tgen(request):
"Setup/Teardown the environment and provide tgen argument to tests"
nrouters = request.param
topodef = {f"sw{i}": (f"r{i}", f"r{i+1}") for i in range(1, nrouters)}
if nrouters == 4:
topodef["sw4"] = ("r4", "r1")
tgen = Topogen(topodef, request.module.__name__)
tgen.start_topology()
router_list = tgen.routers()
for _, router in router_list.items():
router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
router.load_config(TopoRouter.RD_OSPF, "ospfd.conf")
router.net.daemons_options["ospfd"] = "--apiserver"
tgen.start_router()
yield tgen
tgen.stop_topology()
# Fixture that executes before each test
@pytest.fixture(autouse=True)
def skip_on_failure(tgen):
if tgen.routers_have_failure():
pytest.skip("skipped because of previous test failure")
# ------------
# Test Utility
# ------------
@retry(retry_timeout=45)
def verify_ospf_database(tgen, dut, input_dict, cmd="show ip ospf database json"):
del tgen
show_ospf_json = run_frr_cmd(dut, cmd, isjson=True)
if not bool(show_ospf_json):
return "ospf is not running"
result = json_cmp(show_ospf_json, input_dict)
return str(result) if result else None
def myreadline(f):
buf = b""
while True:
# logging.debug("READING 1 CHAR")
c = f.read(1)
if not c:
return buf if buf else None
buf += c
# logging.debug("READ CHAR: '%s'", c)
if c == b"\n":
return buf
def _wait_output(p, regex, maxwait=120):
timeout = Timeout(maxwait)
while not timeout.is_expired():
# line = p.stdout.readline()
line = myreadline(p.stdout)
if not line:
assert None, "EOF waiting for '{}'".format(regex)
line = line.decode("utf-8")
line = line.rstrip()
if line:
logging.debug("GOT LINE: '%s'", line)
m = re.search(regex, line)
if m:
return m
assert None, "Failed to get output matching '{}' withint {} actual {}s".format(
regex, maxwait, timeout.elapsed()
)
# -----
# Tests
# -----
def _test_reachability(tgen, testbin):
waitlist = [
"1.0.0.0,2.0.0.0,4.0.0.0",
"2.0.0.0,4.0.0.0",
"1.0.0.0,2.0.0.0,4.0.0.0",
]
r2 = tgen.gears["r2"]
r3 = tgen.gears["r3"]
r4 = tgen.gears["r4"]
wait_args = [f"--wait={x}" for x in waitlist]
p = None
try:
step("reachable: check for initial reachability")
p = r3.popen(
["/usr/bin/timeout", "120", testbin, "-v", *wait_args],
encoding=None, # don't buffer
stdin=subprocess.DEVNULL,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
_wait_output(p, "SUCCESS: {}".format(waitlist[0]))
step("reachable: check for modified reachability")
interface_set_status(r2, "r2-eth0", False)
interface_set_status(r4, "r4-eth1", False)
_wait_output(p, "SUCCESS: {}".format(waitlist[1]))
step("reachable: check for restored reachability")
interface_set_status(r2, "r2-eth0", True)
interface_set_status(r4, "r4-eth1", True)
_wait_output(p, "SUCCESS: {}".format(waitlist[2]))
except Exception as error:
logging.error("ERROR: %s", error)
raise
finally:
if p:
p.terminate()
p.wait()
@pytest.mark.parametrize("tgen", [4], indirect=True)
def test_ospf_reachability(tgen):
testbin = os.path.join(TESTDIR, "ctester.py")
rc, o, e = tgen.gears["r2"].net.cmd_status([testbin, "--help"])
logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", testbin, rc, o, e)
_test_reachability(tgen, testbin)
def _test_router_id(tgen, testbin):
r1 = tgen.gears["r1"]
waitlist = [
"1.0.0.0",
"1.1.1.1",
"1.0.0.0",
]
mon_args = [f"--monitor={x}" for x in waitlist]
p = None
try:
step("router id: check for initial router id")
p = r1.popen(
["/usr/bin/timeout", "120", testbin, "-v", *mon_args],
encoding=None, # don't buffer
stdin=subprocess.DEVNULL,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
_wait_output(p, "SUCCESS: {}".format(waitlist[0]))
step("router id: check for modified router id")
r1.vtysh_multicmd("conf t\nrouter ospf\nospf router-id 1.1.1.1")
_wait_output(p, "SUCCESS: {}".format(waitlist[1]))
step("router id: check for restored router id")
r1.vtysh_multicmd("conf t\nrouter ospf\nospf router-id 1.0.0.0")
_wait_output(p, "SUCCESS: {}".format(waitlist[2]))
except Exception as error:
logging.error("ERROR: %s", error)
raise
finally:
if p:
p.terminate()
p.wait()
@pytest.mark.parametrize("tgen", [2], indirect=True)
def test_ospf_router_id(tgen):
testbin = os.path.join(TESTDIR, "ctester.py")
rc, o, e = tgen.gears["r1"].net.cmd_status([testbin, "--help"])
logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", testbin, rc, o, e)
_test_router_id(tgen, testbin)
def _test_add_data(tgen, apibin):
"Test adding opaque data to domain"
r1 = tgen.gears["r1"]
step("add opaque: add opaque link local")
p = None
try:
p = r1.popen([apibin, "-v", "add,9,10.0.1.1,230,2,00000202"])
input_dict = {
"routerId": "1.0.0.0",
"areas": {
"1.2.3.4": {
"linkLocalOpaqueLsa": [
{
"lsId": "230.0.0.2",
"advertisedRouter": "1.0.0.0",
"sequenceNumber": "80000001",
}
],
}
},
}
# Wait for it to show up
assert verify_ospf_database(tgen, r1, input_dict) is None
input_dict = {
"linkLocalOpaqueLsa": {
"areas": {
"1.2.3.4": [
{
"linkStateId": "230.0.0.2",
"advertisingRouter": "1.0.0.0",
"lsaSeqNumber": "80000001",
"opaqueData": "00000202",
},
],
}
},
}
# verify content
json_cmd = "show ip ospf da opaque-link json"
assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None
step("reset client, add opaque area, verify link local flushing")
p.send_signal(signal.SIGINT)
time.sleep(2)
p.wait()
p = None
p = r1.popen([apibin, "-v", "add,10,1.2.3.4,231,1,00010101"])
input_dict = {
"routerId": "1.0.0.0",
"areas": {
"1.2.3.4": {
"linkLocalOpaqueLsa": [
{
"lsId": "230.0.0.2",
"advertisedRouter": "1.0.0.0",
"sequenceNumber": "80000001",
"lsaAge": 3600,
}
],
"areaLocalOpaqueLsa": [
{
"lsId": "231.0.0.1",
"advertisedRouter": "1.0.0.0",
"sequenceNumber": "80000001",
},
],
}
},
}
# Wait for it to show up
assert verify_ospf_database(tgen, r1, input_dict) is None
input_dict = {
"areaLocalOpaqueLsa": {
"areas": {
"1.2.3.4": [
{
"linkStateId": "231.0.0.1",
"advertisingRouter": "1.0.0.0",
"lsaSeqNumber": "80000001",
"opaqueData": "00010101",
},
],
}
},
}
# verify content
json_cmd = "show ip ospf da opaque-area json"
assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None
step("reset client, add opaque AS, verify area flushing")
p.send_signal(signal.SIGINT)
time.sleep(2)
p.wait()
p = None
p = r1.popen([apibin, "-v", "add,11,232,3,deadbeaf01234567"])
input_dict = {
"routerId": "1.0.0.0",
"areas": {
"1.2.3.4": {
"areaLocalOpaqueLsa": [
{
"lsId": "231.0.0.1",
"advertisedRouter": "1.0.0.0",
"sequenceNumber": "80000001",
"lsaAge": 3600,
},
],
}
},
"asExternalOpaqueLsa": [
{
"lsId": "232.0.0.3",
"advertisedRouter": "1.0.0.0",
"sequenceNumber": "80000001",
},
],
}
# Wait for it to show up
assert verify_ospf_database(tgen, r1, input_dict) is None
input_dict = {
"asExternalOpaqueLsa": [
{
"linkStateId": "232.0.0.3",
"advertisingRouter": "1.0.0.0",
"lsaSeqNumber": "80000001",
"opaqueData": "deadbeaf01234567",
},
]
}
# verify content
json_cmd = "show ip ospf da opaque-as json"
assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None
step("stop client, verify AS flushing")
p.send_signal(signal.SIGINT)
time.sleep(2)
p.wait()
p = None
input_dict = {
"routerId": "1.0.0.0",
"asExternalOpaqueLsa": [
{
"lsId": "232.0.0.3",
"advertisedRouter": "1.0.0.0",
"sequenceNumber": "80000001",
"lsaAge": 3600,
},
],
}
# Wait for it to be flushed
assert verify_ospf_database(tgen, r1, input_dict) is None
step("start client adding opaque domain, verify new sequence number and data")
# Originate it again
p = r1.popen([apibin, "-v", "add,11,232,3,ebadf00d"])
input_dict = {
"routerId": "1.0.0.0",
"asExternalOpaqueLsa": [
{
"lsId": "232.0.0.3",
"advertisedRouter": "1.0.0.0",
"sequenceNumber": "80000002",
},
],
}
assert verify_ospf_database(tgen, r1, input_dict) is None
input_dict = {
"asExternalOpaqueLsa": [
{
"linkStateId": "232.0.0.3",
"advertisingRouter": "1.0.0.0",
"lsaSeqNumber": "80000002",
"opaqueData": "ebadf00d",
},
]
}
# verify content
json_cmd = "show ip ospf da opaque-as json"
assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None
logging.debug("sending interrupt to writer api client")
p.send_signal(signal.SIGINT)
time.sleep(2)
p.wait()
p = None
except Exception:
if p:
p.terminate()
if p.wait():
comm_error(p)
p = None
raise
finally:
if p:
logging.debug("cleanup: sending interrupt to writer api client")
p.terminate()
p.wait()
@pytest.mark.parametrize("tgen", [2], indirect=True)
def test_ospf_opaque_add_data3(tgen):
apibin = os.path.join(CLIENTDIR, "ospfclient.py")
rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"])
logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e)
_test_add_data(tgen, apibin)
def _test_opaque_add_del(tgen, apibin):
"Test adding opaque data to domain"
r1 = tgen.gears["r1"]
r2 = tgen.gears["r2"]
p = None
pread = None
# Log to our stdin, stderr
pout = open(os.path.join(r1.net.logdir, "r1/add-del.log"), "a+")
try:
step("reachable: check for add notification")
pread = r2.popen(
["/usr/bin/timeout", "120", apibin, "-v", "--logtag=READER", "wait,120"],
encoding=None, # don't buffer
stdin=subprocess.DEVNULL,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
p = r1.popen(
[
apibin,
"-v",
"add,9,10.0.1.1,230,1",
"add,9,10.0.1.1,230,2,00000202",
"wait,1",
"add,10,1.2.3.4,231,1",
"add,10,1.2.3.4,231,2,0102030405060708",
"wait,1",
"add,11,232,1",
"add,11,232,2,ebadf00d",
"wait,20",
"del,9,10.0.1.1,230,2,0",
"del,10,1.2.3.4,231,2,1",
"del,11,232,1,1",
]
)
add_input_dict = {
"areas": {
"1.2.3.4": {
"linkLocalOpaqueLsa": [
{
"lsId": "230.0.0.1",
"advertisedRouter": "1.0.0.0",
"sequenceNumber": "80000001",
"checksum": "76bf",
},
{
"lsId": "230.0.0.2",
"advertisedRouter": "1.0.0.0",
"sequenceNumber": "80000001",
"checksum": "8aa2",
},
],
"linkLocalOpaqueLsaCount": 2,
"areaLocalOpaqueLsa": [
{
"lsId": "231.0.0.1",
"advertisedRouter": "1.0.0.0",
"sequenceNumber": "80000001",
"checksum": "5bd8",
},
{
"lsId": "231.0.0.2",
"advertisedRouter": "1.0.0.0",
"sequenceNumber": "80000001",
"checksum": "7690",
},
],
"areaLocalOpaqueLsaCount": 2,
},
},
"asExternalOpaqueLsa": [
{
"lsId": "232.0.0.1",
"advertisedRouter": "1.0.0.0",
"sequenceNumber": "80000001",
"checksum": "5ed5",
},
{
"lsId": "232.0.0.2",
"advertisedRouter": "1.0.0.0",
"sequenceNumber": "80000001",
"checksum": "d9bd",
},
],
"asExternalOpaqueLsaCount": 2,
}
step("reachable: check for add LSAs")
json_cmd = "show ip ospf da json"
assert verify_ospf_database(tgen, r1, add_input_dict, json_cmd) is None
assert verify_ospf_database(tgen, r2, add_input_dict, json_cmd) is None
numcs = 3
json_cmds = [
"show ip ospf da opaque-link json",
"show ip ospf da opaque-area json",
"show ip ospf da opaque-as json",
]
add_detail_input_dict = [
{
"linkLocalOpaqueLsa": {
"areas": {
"1.2.3.4": [
{
"linkStateId": "230.0.0.1",
"advertisingRouter": "1.0.0.0",
"lsaSeqNumber": "80000001",
"checksum": "76bf",
"length": 20,
"opaqueDataLength": 0,
},
{
"linkStateId": "230.0.0.2",
"advertisingRouter": "1.0.0.0",
"lsaSeqNumber": "80000001",
"checksum": "8aa2",
"length": 24,
"opaqueId": 2,
"opaqueDataLength": 4,
},
]
}
}
},
{
"areaLocalOpaqueLsa": {
"areas": {
"1.2.3.4": [
{
"linkStateId": "231.0.0.1",
"advertisingRouter": "1.0.0.0",
"lsaSeqNumber": "80000001",
"checksum": "5bd8",
"length": 20,
"opaqueDataLength": 0,
},
{
"linkStateId": "231.0.0.2",
"advertisingRouter": "1.0.0.0",
"lsaSeqNumber": "80000001",
"checksum": "7690",
"length": 28,
"opaqueDataLength": 8,
},
],
},
},
},
{
"asExternalOpaqueLsa": [
{
"linkStateId": "232.0.0.1",
"advertisingRouter": "1.0.0.0",
"lsaSeqNumber": "80000001",
"checksum": "5ed5",
"length": 20,
"opaqueDataLength": 0,
},
{
"linkStateId": "232.0.0.2",
"advertisingRouter": "1.0.0.0",
"lsaSeqNumber": "80000001",
"checksum": "d9bd",
"length": 24,
"opaqueDataLength": 4,
},
],
},
]
i = 0
while i < numcs:
step("reachable: check for add LSA details: %s" % json_cmds[i])
assert (
verify_ospf_database(tgen, r1, add_detail_input_dict[i], json_cmds[i])
is None
)
assert (
verify_ospf_database(tgen, r2, add_detail_input_dict[i], json_cmds[i])
is None
)
i += 1
# Wait for add notification
# RECV: LSA update msg for LSA 232.0.0.3 in area 0.0.0.0 seq 0x80000001 len 24 age 9
ls_ids = [
"230.0.0.1",
"230.0.0.2",
"231.0.0.1",
"231.0.0.2",
"232.0.0.1",
"232.0.0.2",
]
for ls_id in ls_ids:
step("reachable: check for API add notification: %s" % ls_id)
waitfor = "RECV:.*update msg.*LSA {}.*age ([0-9]+)".format(ls_id)
_ = _wait_output(pread, waitfor)
del_input_dict = {
"areas": {
"1.2.3.4": {
"linkLocalOpaqueLsa": [
{
"lsId": "230.0.0.1",
"advertisedRouter": "1.0.0.0",
"sequenceNumber": "80000001",
"checksum": "76bf",
},
{
"lsId": "230.0.0.2",
"advertisedRouter": "1.0.0.0",
"lsaAge": 3600,
"sequenceNumber": "80000001",
"checksum": "8aa2",
},
],
"linkLocalOpaqueLsaCount": 2,
"areaLocalOpaqueLsa": [
{
"lsId": "231.0.0.1",
"advertisedRouter": "1.0.0.0",
"sequenceNumber": "80000001",
"checksum": "5bd8",
},
{
"lsId": "231.0.0.2",
"advertisedRouter": "1.0.0.0",
"lsaAge": 3600,
"sequenceNumber": "80000002",
"checksum": "4fe2",
},
],
"areaLocalOpaqueLsaCount": 2,
},
},
"asExternalOpaqueLsa": [
{
"lsId": "232.0.0.1",
"advertisedRouter": "1.0.0.0",
"lsaAge": 3600,
"sequenceNumber": "80000001",
"checksum": "5ed5",
},
{
"lsId": "232.0.0.2",
"advertisedRouter": "1.0.0.0",
"sequenceNumber": "80000001",
"checksum": "d9bd",
},
],
"asExternalOpaqueLsaCount": 2,
}
step("reachable: check for explicit withdrawal LSAs")
json_cmd = "show ip ospf da json"
assert verify_ospf_database(tgen, r1, del_input_dict, json_cmd) is None
assert verify_ospf_database(tgen, r2, del_input_dict, json_cmd) is None
del_detail_input_dict = [
{
"linkLocalOpaqueLsa": {
"areas": {
"1.2.3.4": [
{
"linkStateId": "230.0.0.1",
"advertisingRouter": "1.0.0.0",
"lsaSeqNumber": "80000001",
"checksum": "76bf",
"length": 20,
"opaqueDataLength": 0,
},
{
"linkStateId": "230.0.0.2",
"advertisingRouter": "1.0.0.0",
"lsaAge": 3600,
"lsaSeqNumber": "80000001",
"checksum": "8aa2",
"length": 24,
"opaqueId": 2,
"opaqueDataLength": 4,
},
]
}
}
},
{
"areaLocalOpaqueLsa": {
"areas": {
"1.2.3.4": [
{
"linkStateId": "231.0.0.1",
"advertisingRouter": "1.0.0.0",
"lsaSeqNumber": "80000001",
"checksum": "5bd8",
"length": 20,
"opaqueDataLength": 0,
},
{
"lsaAge": 3600,
"linkStateId": "231.0.0.2",
"advertisingRouter": "1.0.0.0",
"lsaSeqNumber": "80000002",
"checksum": "4fe2",
# data removed
"length": 20,
"opaqueDataLength": 0,
},
],
},
},
},
{
"asExternalOpaqueLsa": [
{
"linkStateId": "232.0.0.1",
"advertisingRouter": "1.0.0.0",
"lsaAge": 3600,
"lsaSeqNumber": "80000001",
"checksum": "5ed5",
"length": 20,
"opaqueDataLength": 0,
},
{
"linkStateId": "232.0.0.2",
"advertisingRouter": "1.0.0.0",
"lsaSeqNumber": "80000001",
"checksum": "d9bd",
"length": 24,
"opaqueDataLength": 4,
},
],
},
]
i = 0
while i < numcs:
step("reachable: check for delete LSA details: %s" % json_cmds[i])
assert (
verify_ospf_database(tgen, r1, del_detail_input_dict[i], json_cmds[i])
is None
)
assert (
verify_ospf_database(tgen, r2, del_detail_input_dict[i], json_cmds[i])
is None
)
i += 1
p.terminate()
if p.wait():
comm_error(p)
del_detail_input_dict = [
{
"linkLocalOpaqueLsa": {
"areas": {
"1.2.3.4": [
{
"linkStateId": "230.0.0.1",
"advertisingRouter": "1.0.0.0",
"lsaAge": 3600,
"lsaSeqNumber": "80000001",
"checksum": "76bf",
"length": 20,
"opaqueDataLength": 0,
},
{
"linkStateId": "230.0.0.2",
"advertisingRouter": "1.0.0.0",
"lsaAge": 3600,
"lsaSeqNumber": "80000001",
"checksum": "8aa2",
"length": 24,
"opaqueId": 2,
"opaqueDataLength": 4,
},
]
}
}
},
{
"areaLocalOpaqueLsa": {
"areas": {
"1.2.3.4": [
{
"lsaAge": 3600,
"linkStateId": "231.0.0.1",
"advertisingRouter": "1.0.0.0",
"lsaSeqNumber": "80000001",
"checksum": "5bd8",
"length": 20,
"opaqueDataLength": 0,
},
{
"lsaAge": 3600,
"linkStateId": "231.0.0.2",
"advertisingRouter": "1.0.0.0",
"lsaSeqNumber": "80000002",
"checksum": "4fe2",
# data removed
"length": 20,
"opaqueDataLength": 0,
},
],
},
},
},
{
"asExternalOpaqueLsa": [
{
"linkStateId": "232.0.0.1",
"advertisingRouter": "1.0.0.0",
"lsaAge": 3600,
"lsaSeqNumber": "80000001",
"checksum": "5ed5",
"length": 20,
"opaqueDataLength": 0,
},
{
"linkStateId": "232.0.0.2",
"advertisingRouter": "1.0.0.0",
"lsaAge": 3600,
"lsaSeqNumber": "80000001",
"checksum": "d9bd",
"length": 24,
"opaqueDataLength": 4,
},
],
},
]
i = 0
while i < numcs:
step(
"reachable: check for post API shutdown delete LSA details: %s"
% json_cmds[i]
)
assert (
verify_ospf_database(tgen, r1, del_detail_input_dict[i], json_cmds[i])
is None
)
assert (
verify_ospf_database(tgen, r2, del_detail_input_dict[i], json_cmds[i])
is None
)
i += 1
# step("reachable: check for flush/age out")
# # Wait for max age notification
# waitfor = "RECV:.*update msg.*LSA {}.*age 3600".format(ls_id)
# _wait_output(pread, waitfor)
ls_ids = [
"230.0.0.2",
"231.0.0.2",
"232.0.0.1",
"230.0.0.1",
"231.0.0.1",
"232.0.0.2",
]
for ls_id in ls_ids:
step("reachable: check for API delete notification: %s" % ls_id)
waitfor = "RECV:.*delete msg.*LSA {}.*age".format(ls_id)
_ = _wait_output(pread, waitfor)
except Exception:
if p:
p.terminate()
if p.wait():
comm_error(p)
p = None
raise
finally:
if pread:
pread.terminate()
pread.wait()
if p:
p.terminate()
p.wait()
@pytest.mark.parametrize("tgen", [2], indirect=True)
def test_ospf_opaque_delete_data3(tgen):
apibin = os.path.join(CLIENTDIR, "ospfclient.py")
rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"])
logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e)
_test_opaque_add_del(tgen, apibin)
def _test_opaque_add_restart_add(tgen, apibin):
"Test adding an opaque LSA and then restarting ospfd"
r1 = tgen.gears["r1"]
r2 = tgen.gears["r2"]
p = None
pread = None
# Log to our stdin, stderr
pout = open(os.path.join(r1.net.logdir, "r1/add-del.log"), "a+")
try:
step("reachable: check for add notification")
pread = r2.popen(
["/usr/bin/timeout", "120", apibin, "-v", "--logtag=READER", "wait,120"],
encoding=None, # don't buffer
stdin=subprocess.DEVNULL,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
p = r1.popen(
[
apibin,
"-v",
"add,10,1.2.3.4,231,1",
"add,10,1.2.3.4,231,1,feedaceebeef",
"wait, 5",
"add,10,1.2.3.4,231,1,feedaceedeadbeef",
"wait, 5",
"add,10,1.2.3.4,231,1,feedaceebaddbeef",
"wait, 5",
]
)
add_input_dict = {
"areas": {
"1.2.3.4": {
"areaLocalOpaqueLsa": [
{
"lsId": "231.0.0.1",
"advertisedRouter": "1.0.0.0",
"sequenceNumber": "80000004",
"checksum": "3128",
},
],
"areaLocalOpaqueLsaCount": 1,
},
},
}
step("Check for add LSAs")
json_cmd = "show ip ospf da json"
assert verify_ospf_database(tgen, r1, add_input_dict, json_cmd) is None
assert verify_ospf_database(tgen, r2, add_input_dict, json_cmd) is None
step("Shutdown the interface on r1 to isolate it for r2")
shutdown_bringup_interface(tgen, "r1", "r1-eth0", False)
time.sleep(2)
step("Reset the client")
p.send_signal(signal.SIGINT)
time.sleep(2)
p.wait()
p = None
step("Kill ospfd on R1")
kill_router_daemons(tgen, "r1", ["ospfd"])
time.sleep(2)
step("Bring ospfd on R1 back up")
start_router_daemons(tgen, "r1", ["ospfd"])
p = r1.popen(
[
apibin,
"-v",
"add,10,1.2.3.4,231,1",
"add,10,1.2.3.4,231,1,feedaceecafebeef",
"wait, 5",
]
)
step("Bring the interface on r1 back up for connection to r2")
shutdown_bringup_interface(tgen, "r1", "r1-eth0", True)
step("Verify area opaque LSA refresh")
json_cmd = "show ip ospf da opaque-area json"
add_detail_input_dict = {
"areaLocalOpaqueLsa": {
"areas": {
"1.2.3.4": [
{
"linkStateId": "231.0.0.1",
"advertisingRouter": "1.0.0.0",
"lsaSeqNumber": "80000005",
"checksum": "a87e",
"length": 28,
"opaqueDataLength": 8,
},
],
},
},
}
assert verify_ospf_database(tgen, r1, add_detail_input_dict, json_cmd) is None
assert verify_ospf_database(tgen, r2, add_detail_input_dict, json_cmd) is None
step("Shutdown the interface on r1 to isolate it for r2")
shutdown_bringup_interface(tgen, "r1", "r1-eth0", False)
time.sleep(2)
step("Reset the client")
p.send_signal(signal.SIGINT)
time.sleep(2)
p.wait()
p = None
step("Kill ospfd on R1")
kill_router_daemons(tgen, "r1", ["ospfd"])
time.sleep(2)
step("Bring ospfd on R1 back up")
start_router_daemons(tgen, "r1", ["ospfd"])
step("Bring the interface on r1 back up for connection to r2")
shutdown_bringup_interface(tgen, "r1", "r1-eth0", True)
step("Verify area opaque LSA Purging")
json_cmd = "show ip ospf da opaque-area json"
add_detail_input_dict = {
"areaLocalOpaqueLsa": {
"areas": {
"1.2.3.4": [
{
"lsaAge": 3600,
"linkStateId": "231.0.0.1",
"advertisingRouter": "1.0.0.0",
"lsaSeqNumber": "80000005",
"checksum": "a87e",
"length": 28,
"opaqueDataLength": 8,
},
],
},
},
}
assert verify_ospf_database(tgen, r1, add_detail_input_dict, json_cmd) is None
assert verify_ospf_database(tgen, r2, add_detail_input_dict, json_cmd) is None
step("Verify Area Opaque LSA removal after timeout (60 seconds)")
time.sleep(60)
json_cmd = "show ip ospf da opaque-area json"
timeout_detail_input_dict = {
"areaLocalOpaqueLsa": {
"areas": {
"1.2.3.4": [],
},
},
}
assert (
verify_ospf_database(tgen, r1, timeout_detail_input_dict, json_cmd) is None
)
assert (
verify_ospf_database(tgen, r2, timeout_detail_input_dict, json_cmd) is None
)
except Exception:
if p:
p.terminate()
if p.wait():
comm_error(p)
p = None
raise
finally:
if pread:
pread.terminate()
pread.wait()
if p:
p.terminate()
p.wait()
@pytest.mark.parametrize("tgen", [2], indirect=True)
def test_ospf_opaque_restart(tgen):
apibin = os.path.join(CLIENTDIR, "ospfclient.py")
rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"])
logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e)
_test_opaque_add_restart_add(tgen, apibin)
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))