mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-05-22 08:37:29 +00:00

Add support for "[no] ip ospf capbility opaque" at the interface level with the default being capability opaque enabled. The command "no ip ospf capability opaque" will disable opaque LSA database exchange and flooding on the interface. A change in configuration will result in the interface being flapped to update our options for neighbors but no attempt will be made to purge existing LSAs as in dense topologies, these may received by neighbors through different interfaces. Topotests are added to test both the configuration and the LSA opaque flooding suppression. Signed-off-by: Acee <aceelindem@gmail.com>
1587 lines
56 KiB
Python
1587 lines
56 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
|
|
from functools import partial
|
|
|
|
import pytest
|
|
from lib.common_config import (
|
|
kill_router_daemons,
|
|
retry,
|
|
run_frr_cmd,
|
|
shutdown_bringup_interface,
|
|
start_router_daemons,
|
|
step,
|
|
)
|
|
from lib.micronet import Timeout, comm_error
|
|
from lib.topogen import Topogen, TopoRouter
|
|
from lib.topotest import interface_set_status, json_cmp
|
|
|
|
# pylint: disable=C0413
|
|
# Import topogen and topotest helpers
|
|
from lib import topotest
|
|
from lib.topogen import Topogen, TopoRouter, get_topogen
|
|
from lib.topolog import logger
|
|
|
|
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", # seq = 80000001
|
|
"wait, 5",
|
|
"add,10,1.2.3.4,231,1,feedaceebeef", # seq = 80000002
|
|
"wait, 5",
|
|
]
|
|
)
|
|
add_input_dict = {
|
|
"areas": {
|
|
"1.2.3.4": {
|
|
"areaLocalOpaqueLsa": [
|
|
{
|
|
"lsId": "231.0.0.1",
|
|
"advertisedRouter": "1.0.0.0",
|
|
"sequenceNumber": "80000002",
|
|
"checksum": "cd26",
|
|
},
|
|
],
|
|
"areaLocalOpaqueLsaCount": 1,
|
|
},
|
|
},
|
|
}
|
|
step("Wait for the Opaque LSA to be distributed")
|
|
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
|
|
|
|
# Verify the OLD LSA is still there unchanged on R2
|
|
assert verify_ospf_database(tgen, r2, add_input_dict, json_cmd) is 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"])
|
|
|
|
# This will start off with sequence num 80000001
|
|
# But should advance to 80000003 when we reestablish with r2
|
|
p = r1.popen(
|
|
[
|
|
apibin,
|
|
"-v",
|
|
"add,10,1.2.3.4,231,1,feedaceecafebeef", # seq=80000001
|
|
"wait, 5",
|
|
]
|
|
)
|
|
|
|
# verify the old value on r2 doesn't change yet
|
|
time.sleep(2)
|
|
assert verify_ospf_database(tgen, r2, add_input_dict, json_cmd) is None
|
|
|
|
json_cmd = "show ip ospf da opaque-area json"
|
|
new_add_input_dict = {
|
|
"areaLocalOpaqueLsa": {
|
|
"areas": {
|
|
"1.2.3.4": [
|
|
{
|
|
"linkStateId": "231.0.0.1",
|
|
"advertisingRouter": "1.0.0.0",
|
|
"lsaSeqNumber": "80000001",
|
|
"checksum": "b07a",
|
|
"length": 28,
|
|
"opaqueDataLength": 8,
|
|
},
|
|
],
|
|
},
|
|
},
|
|
}
|
|
# verify new value with initial seq number on r1
|
|
assert verify_ospf_database(tgen, r1, new_add_input_dict, json_cmd) is None
|
|
|
|
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")
|
|
|
|
# Update the expected value to sequence number rev and new checksum
|
|
update_dict = new_add_input_dict["areaLocalOpaqueLsa"]["areas"]["1.2.3.4"][0]
|
|
update_dict["lsaSeqNumber"] = "80000003"
|
|
update_dict["checksum"] = "cb27"
|
|
|
|
# should settle on the same value now.
|
|
assert verify_ospf_database(tgen, r1, new_add_input_dict, json_cmd) is None
|
|
assert verify_ospf_database(tgen, r2, new_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"])
|
|
|
|
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": "80000003",
|
|
"checksum": "cb27",
|
|
"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)
|
|
|
|
|
|
def _test_opaque_interface_disable(tgen, apibin):
|
|
"Test disabling opaque capability on an interface"
|
|
|
|
r1 = tgen.gears["r1"]
|
|
r2 = tgen.gears["r2"]
|
|
tc_name = "opaque_interface_disable"
|
|
|
|
p = None
|
|
pread = None
|
|
# Log to our stdin, stderr
|
|
pout = open(os.path.join(r1.net.logdir, "r1/intf-disable.log"), "a+")
|
|
try:
|
|
# STEP 1 in test_ospf_opaque_interface_disable and STEP 56 in CI tests
|
|
step("Disable OSPF opaque LSA Copability on r1's interface to r2")
|
|
r1.vtysh_multicmd("conf t\ninterface r1-eth0\nno ip ospf capability opaque")
|
|
time.sleep(15)
|
|
|
|
# STEP 2 in test_ospf_opaque_interface_disable and STEP 57 in CI tests
|
|
step("Verify the r1 configuration of 'no ip ospf capability opaque'")
|
|
no_capability_opaque_cfg = (
|
|
tgen.net["r1"]
|
|
.cmd(
|
|
'vtysh -c "show running ospfd" | grep "^ no ip ospf capability opaque"'
|
|
)
|
|
.rstrip()
|
|
)
|
|
assertmsg = (
|
|
"'no ip ospf capability opaque' applied, but not present in configuration"
|
|
)
|
|
assert no_capability_opaque_cfg == " no ip ospf capability opaque", assertmsg
|
|
|
|
# STEP 3 in test_ospf_opaque_interface_disable and STEP 58 in CI tests
|
|
step("Verify the ospf opaque option is not applied to the r1 interface")
|
|
r1_interface_without_opaque = {
|
|
"interfaces": {
|
|
"r1-eth0": {
|
|
"ifUp": True,
|
|
"ospfEnabled": True,
|
|
"ipAddress": "10.0.1.1",
|
|
"ospfIfType": "Broadcast",
|
|
"opaqueCapable": False,
|
|
}
|
|
}
|
|
}
|
|
r1_interface_with_opaque = {
|
|
"interfaces": {
|
|
"r1-eth0": {
|
|
"ifUp": True,
|
|
"ospfEnabled": True,
|
|
"ipAddress": "10.0.1.1",
|
|
"ospfIfType": "Broadcast",
|
|
"opaqueCapable": True,
|
|
}
|
|
}
|
|
}
|
|
test_func = partial(
|
|
topotest.router_json_cmp, r1, "show ip ospf interface json", r1_interface_without_opaque
|
|
)
|
|
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
|
|
assertmsg = "r1 OSPF interface doesn't have opaque capability disabled"
|
|
assert result is None, assertmsg
|
|
|
|
r1_neighbor_without_opaque = {
|
|
"neighbors": {
|
|
"2.0.0.0": [
|
|
{
|
|
"optionsList": "*|-|-|-|-|-|E|-",
|
|
}
|
|
]
|
|
}
|
|
}
|
|
r2_neighbor_without_opaque = {
|
|
"neighbors": {
|
|
"1.0.0.0": [
|
|
{
|
|
"optionsList": "*|-|-|-|-|-|E|-",
|
|
}
|
|
]
|
|
}
|
|
}
|
|
# STEP 4 in test_ospf_opaque_interface_disable and STEP 59 in CI tests
|
|
step("Verify that the r1 neighbor options don't include opaque")
|
|
test_func = partial(
|
|
topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_without_opaque
|
|
)
|
|
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
|
|
assertmsg = "r1 OSPF neighbor has opaque option in optionsList"
|
|
assert result is None, assertmsg
|
|
|
|
# STEP 5 in test_ospf_opaque_interface_disable and STEP 60 in CI tests
|
|
step("Verify that the r1 neighbor options don't include opaque")
|
|
test_func = partial(
|
|
topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_without_opaque
|
|
)
|
|
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
|
|
assertmsg = "r2 OSPF neighbor has opaque option in optionsList"
|
|
assert result is None, assertmsg
|
|
|
|
# STEP 6 in test_ospf_opaque_interface_disable and STEP 61 in CI tests
|
|
step(
|
|
"Verify no r2 configuration of 'no ip ospf capability opaque' in r2 configuration"
|
|
)
|
|
rc, _, _ = tgen.net["r2"].cmd_status(
|
|
"show running ospfd | grep -q 'ip ospf capability opaque'", warn=False
|
|
)
|
|
assertmsg = "'no ip ospf capability opaque' not applied, but not present in r2 configuration"
|
|
assert rc, assertmsg
|
|
|
|
# STEP 7 in test_ospf_opaque_interface_disable and STEP 62 in CI tests
|
|
step("Verify the ospf opaque option is applied to the r2 interface")
|
|
r2_interface_without_opaque = {
|
|
"interfaces": {
|
|
"r2-eth0": {
|
|
"ifUp": True,
|
|
"ospfEnabled": True,
|
|
"ipAddress": "10.0.1.2",
|
|
"ospfIfType": "Broadcast",
|
|
"opaqueCapable": False,
|
|
}
|
|
}
|
|
}
|
|
r2_interface_with_opaque = {
|
|
"interfaces": {
|
|
"r2-eth0": {
|
|
"ifUp": True,
|
|
"ospfEnabled": True,
|
|
"ipAddress": "10.0.1.2",
|
|
"ospfIfType": "Broadcast",
|
|
"opaqueCapable": True,
|
|
}
|
|
}
|
|
}
|
|
test_func = partial(
|
|
topotest.router_json_cmp, r2, "show ip ospf interface json", r2_interface_with_opaque
|
|
)
|
|
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
|
|
assertmsg = "r2 OSPF interface has opaque capability disabled"
|
|
assert result is None, assertmsg
|
|
|
|
# STEP 8 in test_ospf_opaque_interface_disable and STEP 63 in CI tests
|
|
step("Install opaque LSAs on r1")
|
|
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,feedaceedeadbeef",
|
|
"add,10,1.2.3.4,231,1,feedaceecafebeef",
|
|
"add,11,232,1,feedaceebaddbeef",
|
|
"wait,20",
|
|
]
|
|
)
|
|
opaque_LSAs_in_database = {
|
|
"areas": {
|
|
"1.2.3.4": {
|
|
"linkLocalOpaqueLsa": [
|
|
{
|
|
"lsId": "230.0.0.1",
|
|
"advertisedRouter": "1.0.0.0",
|
|
"sequenceNumber": "80000001",
|
|
},
|
|
],
|
|
"linkLocalOpaqueLsaCount": 1,
|
|
"areaLocalOpaqueLsa": [
|
|
{
|
|
"lsId": "231.0.0.1",
|
|
"advertisedRouter": "1.0.0.0",
|
|
"sequenceNumber": "80000001",
|
|
},
|
|
],
|
|
"areaLocalOpaqueLsaCount": 1,
|
|
},
|
|
},
|
|
"asExternalOpaqueLsa": [
|
|
{
|
|
"lsId": "232.0.0.1",
|
|
"advertisedRouter": "1.0.0.0",
|
|
"sequenceNumber": "80000001",
|
|
},
|
|
],
|
|
"asExternalOpaqueLsaCount": 1,
|
|
}
|
|
opaque_area_empty_database = {
|
|
"routerId":"2.0.0.0",
|
|
"areaLocalOpaqueLsa":{
|
|
"areas":{
|
|
"1.2.3.4":[
|
|
]
|
|
}
|
|
}
|
|
}
|
|
|
|
# STEP 9 in test_ospf_opaque_interface_disable and STEP 64 in CI tests
|
|
step("Check that LSAs are added on r1")
|
|
test_func = partial(
|
|
topotest.router_json_cmp, r1, "show ip ospf database json", opaque_LSAs_in_database
|
|
)
|
|
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
|
|
assertmsg = "r1 OSPF database doesn't contain opaque LSAs"
|
|
assert result is None, assertmsg
|
|
|
|
# STEP 10 in test_ospf_opaque_interface_disable and STEP 65 in CI tests
|
|
step("Check that LSAs are not added on r2")
|
|
test_func = partial(
|
|
topotest.router_json_cmp, r2, "show ip ospf database opaque-area json",
|
|
opaque_area_empty_database, True
|
|
)
|
|
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
|
|
assertmsg = "r2 OSPF area database contains opaque LSAs"
|
|
assert result is None, assertmsg
|
|
|
|
# STEP 11 in test_ospf_opaque_interface_disable and STEP 66 in CI tests
|
|
step("Enable OSPF opaque LSA Copability on r1's interface to r2")
|
|
r1.vtysh_multicmd("conf t\ninterface r1-eth0\nip ospf capability opaque")
|
|
time.sleep(15)
|
|
|
|
# STEP 12 in test_ospf_opaque_interface_disable and STEP 67 in CI tests
|
|
step("Verify no r1 configuration of 'no ip ospf capability opaque'")
|
|
rc, _, _ = tgen.net["r1"].cmd_status(
|
|
"show running ospfd | grep -q 'ip ospf capability opaque'", warn=False
|
|
)
|
|
assertmsg = "'no ip ospf capability opaque' not applied, but not present in r1 configuration"
|
|
assert rc, assertmsg
|
|
|
|
# STEP 13 in test_ospf_opaque_interface_disable and STEP 68 in CI tests
|
|
step("Verify the ospf opaque option is applied to the r1 interface")
|
|
test_func = partial(
|
|
topotest.router_json_cmp, r1, "show ip ospf interface json", r1_interface_with_opaque
|
|
)
|
|
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
|
|
assertmsg = "r1 OSPF interface doesn't have opaque capability disabled"
|
|
assert result is None, assertmsg
|
|
|
|
r1_neighbor_with_opaque = {
|
|
"neighbors": {
|
|
"2.0.0.0": [
|
|
{
|
|
"optionsList": "*|O|-|-|-|-|E|-",
|
|
}
|
|
]
|
|
}
|
|
}
|
|
r2_neighbor_with_opaque = {
|
|
"neighbors": {
|
|
"1.0.0.0": [
|
|
{
|
|
"optionsList": "*|O|-|-|-|-|E|-",
|
|
}
|
|
]
|
|
}
|
|
}
|
|
# STEP 14 in test_ospf_opaque_interface_disable and STEP 69 in CI tests
|
|
step("Verify that the r1 neighbor options include opaque")
|
|
test_func = partial(
|
|
topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_with_opaque
|
|
)
|
|
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
|
|
assertmsg = "r1 OSPF neighbor doesn't have opaque option in optionsList"
|
|
assert result is None, assertmsg
|
|
|
|
# STEP 15 in test_ospf_opaque_interface_disable and STEP 70 in CI tests
|
|
step("Verify that the r2 neighbor options include opaque")
|
|
test_func = partial(
|
|
topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_with_opaque
|
|
)
|
|
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
|
|
assertmsg = "r2 OSPF neighbor doesn't have opaque option in optionsList"
|
|
assert result is None, assertmsg
|
|
|
|
# STEP 16 in test_ospf_opaque_interface_disable and STEP 71 in CI tests
|
|
step("Check that LSAs are now added to r2")
|
|
test_func = partial(
|
|
topotest.router_json_cmp, r2, "show ip ospf database json", opaque_LSAs_in_database
|
|
)
|
|
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
|
|
assertmsg = "r2 OSPF database doesn't contains opaque LSAs"
|
|
assert result is None, assertmsg
|
|
|
|
# STEP 17 in test_ospf_opaque_interface_disable and STEP 72 in CI tests
|
|
step(
|
|
"Disable Opaque Capability on r2's interface to r1 using the interface address"
|
|
)
|
|
r2.vtysh_multicmd(
|
|
"conf t\ninterface r2-eth0\nno ip ospf capability opaque 10.0.1.2"
|
|
)
|
|
|
|
# STEP 18 in test_ospf_opaque_interface_disable and STEP 73 in CI tests
|
|
step("Clear the OSPF process on r2 to clear the OSPF LSDB")
|
|
r2.vtysh_multicmd("clear ip ospf process")
|
|
time.sleep(15)
|
|
|
|
# STEP 19 in test_ospf_opaque_interface_disable and STEP 74 in CI tests
|
|
step("Verify the r2 configuration of 'no ip ospf capability opaque 10.0.1.2'")
|
|
no_capability_opaque_cfg = (
|
|
tgen.net["r2"]
|
|
.cmd_nostatus(
|
|
'vtysh -c "show running ospfd" | grep "^ no ip ospf capability opaque 10.0.1.2"'
|
|
)
|
|
.rstrip()
|
|
)
|
|
assertmsg = "'no ip ospf capability opaque 10.0.1.2' applied, but not present in configuration"
|
|
assert (
|
|
no_capability_opaque_cfg == " no ip ospf capability opaque 10.0.1.2"
|
|
), assertmsg
|
|
|
|
# STEP 20 in test_ospf_opaque_interface_disable and STEP 75 in CI tests
|
|
step("Verify the ospf opaque option is not applied to the r2 interface")
|
|
test_func = partial(
|
|
topotest.router_json_cmp, r2, "show ip ospf interface json", r2_interface_without_opaque
|
|
)
|
|
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
|
|
assertmsg = "r1 OSPF interface doesn't have opaque capability disabled"
|
|
assert result is None, assertmsg
|
|
|
|
# STEP 21 in test_ospf_opaque_interface_disable and STEP 76 in CI tests
|
|
step("Verify that the r1 neighbor options don't include opaque")
|
|
test_func = partial(
|
|
topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_without_opaque
|
|
)
|
|
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
|
|
assertmsg = "r1 OSPF neighbor has opaque option in optionsList"
|
|
assert result is None, assertmsg
|
|
|
|
# STEP 22 in test_ospf_opaque_interface_disable and STEP 77 in CI tests
|
|
step("Verify that the r2 neighbor options don't include opaque")
|
|
test_func = partial(
|
|
topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_without_opaque
|
|
)
|
|
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
|
|
assertmsg = "r2 OSPF neighbor has opaque option in optionsList"
|
|
assert result is None, assertmsg
|
|
|
|
# STEP 23 in test_ospf_opaque_interface_disable and STEP 78 in CI tests
|
|
step("Verify that r1 still has the opaque LSAs")
|
|
test_func = partial(
|
|
topotest.router_json_cmp, r1, "show ip ospf database json", opaque_LSAs_in_database
|
|
)
|
|
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
|
|
assertmsg = "r1 OSPF database doesn't contain opaque LSAs"
|
|
assert result is None, assertmsg
|
|
|
|
# STEP 24 in test_ospf_opaque_interface_disable and STEP 79 in CI tests
|
|
step("Verify that r2 doesn't have the opaque LSAs")
|
|
test_func = partial(
|
|
topotest.router_json_cmp, r2, "show ip ospf database opaque-area json",
|
|
opaque_area_empty_database, True
|
|
)
|
|
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
|
|
assertmsg = "r2 OSPF area database contains opaque LSAs"
|
|
assert result is None, assertmsg
|
|
|
|
# STEP 25 in test_ospf_opaque_interface_disable and STEP 80 in CI tests
|
|
step("Remove the 'no ip ospf capability opaque 10.0.1.2' config from r2 ")
|
|
r2.vtysh_multicmd(
|
|
"conf t\ninterface r2-eth0\nip ospf capability opaque 10.0.1.2"
|
|
)
|
|
time.sleep(15)
|
|
|
|
# STEP 26 in test_ospf_opaque_interface_disable and STEP 81 in CI tests
|
|
step("Verify the r2 removal of 'no ip ospf capability opaque 10.0.1.2'")
|
|
rc, _, _ = tgen.net["r2"].cmd_status(
|
|
"show running ospfd | grep -q 'ip ospf capability opaque'", warn=False
|
|
)
|
|
assertmsg = "'no ip ospf capability opaque' not applied, but not present in r2 configuration"
|
|
assert rc, assertmsg
|
|
|
|
# STEP 27 in test_ospf_opaque_interface_disable and STEP 82 in CI tests
|
|
step("Verify the ospf opaque option is applied to the r2 interface")
|
|
test_func = partial(
|
|
topotest.router_json_cmp, r2, "show ip ospf interface json", r2_interface_with_opaque
|
|
)
|
|
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
|
|
assertmsg = "r2 OSPF interface doesn't have opaque capability disabled"
|
|
assert result is None, assertmsg
|
|
|
|
# STEP 28 in test_ospf_opaque_interface_disable and STEP 83 in CI tests
|
|
step("Verify that the r2 neighbor options include opaque")
|
|
test_func = partial(
|
|
topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_with_opaque
|
|
)
|
|
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
|
|
assertmsg = "r2 OSPF neighbor doesn't have opaque option in optionsList"
|
|
assert result is None, assertmsg
|
|
|
|
# STEP 29 in test_ospf_opaque_interface_disable and STEP 84 in CI tests
|
|
step("Verify that the r1 neighbor options include opaque")
|
|
test_func = partial(
|
|
topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_with_opaque
|
|
)
|
|
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
|
|
assertmsg = "r1 OSPF neighbor doesn't have opaque option in optionsList"
|
|
assert result is None, assertmsg
|
|
|
|
# STEP 30 in test_ospf_opaque_interface_disable and STEP 85 in CLI tests
|
|
step("Verify that r2 now has the opaque LSAs")
|
|
test_func = partial(
|
|
topotest.router_json_cmp, r2, "show ip ospf database json", opaque_LSAs_in_database
|
|
)
|
|
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
|
|
assertmsg = "r2 OSPF database doesn't contain opaque LSAs"
|
|
assert result is None, assertmsg
|
|
|
|
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_interface_disable(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_interface_disable(tgen, apibin)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
args = ["-s"] + sys.argv[1:]
|
|
sys.exit(pytest.main(args))
|