mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-14 16:04:49 +00:00
isis-topo1: test ISIS topology convergence
Add function to parse 'show isis topology' and expect the correct convergence result. Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
This commit is contained in:
parent
74bd8dd9ba
commit
67f1e9ed09
28
tests/topotests/isis-topo1/r1/r1_topology.json
Normal file
28
tests/topotests/isis-topo1/r1/r1_topology.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"1": {
|
||||
"level-1": [
|
||||
{
|
||||
"vertex": "r1"
|
||||
}
|
||||
],
|
||||
"level-2": [
|
||||
{
|
||||
"vertex": "r1"
|
||||
},
|
||||
{
|
||||
"metric": "internal",
|
||||
"parent": "0",
|
||||
"type": "IP",
|
||||
"vertex": "10.0.20.0/24"
|
||||
},
|
||||
{
|
||||
"interface": "r1-eth0",
|
||||
"metric": "10",
|
||||
"next-hop": "r3",
|
||||
"parent": "r1(4)",
|
||||
"type": "TE-IS",
|
||||
"vertex": "r3"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
28
tests/topotests/isis-topo1/r2/r2_topology.json
Normal file
28
tests/topotests/isis-topo1/r2/r2_topology.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"1": {
|
||||
"level-1": [
|
||||
{
|
||||
"vertex": "r2"
|
||||
}
|
||||
],
|
||||
"level-2": [
|
||||
{
|
||||
"vertex": "r2"
|
||||
},
|
||||
{
|
||||
"metric": "internal",
|
||||
"parent": "0",
|
||||
"type": "IP",
|
||||
"vertex": "10.0.21.0/24"
|
||||
},
|
||||
{
|
||||
"interface": "r2-eth0",
|
||||
"metric": "10",
|
||||
"next-hop": "r4",
|
||||
"parent": "r2(4)",
|
||||
"type": "TE-IS",
|
||||
"vertex": "r4"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
42
tests/topotests/isis-topo1/r3/r3_topology.json
Normal file
42
tests/topotests/isis-topo1/r3/r3_topology.json
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"1": {
|
||||
"level-1": [
|
||||
{
|
||||
"vertex": "r3"
|
||||
},
|
||||
{
|
||||
"metric": "internal",
|
||||
"parent": "0",
|
||||
"type": "IP",
|
||||
"vertex": "10.0.10.0/24"
|
||||
},
|
||||
{
|
||||
"interface": "r3-eth1",
|
||||
"metric": "10",
|
||||
"next-hop": "r5",
|
||||
"parent": "r3(4)",
|
||||
"type": "TE-IS",
|
||||
"vertex": "r5"
|
||||
}
|
||||
],
|
||||
"level-2": [
|
||||
{
|
||||
"vertex": "r3"
|
||||
},
|
||||
{
|
||||
"metric": "internal",
|
||||
"parent": "0",
|
||||
"type": "IP",
|
||||
"vertex": "10.0.20.0/24"
|
||||
},
|
||||
{
|
||||
"interface": "r3-eth0",
|
||||
"metric": "10",
|
||||
"next-hop": "r1",
|
||||
"parent": "r3(4)",
|
||||
"type": "TE-IS",
|
||||
"vertex": "r1"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
42
tests/topotests/isis-topo1/r4/r4_topology.json
Normal file
42
tests/topotests/isis-topo1/r4/r4_topology.json
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"1": {
|
||||
"level-1": [
|
||||
{
|
||||
"vertex": "r4"
|
||||
},
|
||||
{
|
||||
"metric": "internal",
|
||||
"parent": "0",
|
||||
"type": "IP",
|
||||
"vertex": "10.0.11.0/24"
|
||||
},
|
||||
{
|
||||
"interface": "r4-eth1",
|
||||
"metric": "10",
|
||||
"next-hop": "r5",
|
||||
"parent": "r4(4)",
|
||||
"type": "TE-IS",
|
||||
"vertex": "r5"
|
||||
}
|
||||
],
|
||||
"level-2": [
|
||||
{
|
||||
"vertex": "r4"
|
||||
},
|
||||
{
|
||||
"metric": "internal",
|
||||
"parent": "0",
|
||||
"type": "IP",
|
||||
"vertex": "10.0.21.0/24"
|
||||
},
|
||||
{
|
||||
"interface": "r4-eth0",
|
||||
"metric": "10",
|
||||
"next-hop": "r2",
|
||||
"parent": "r4(4)",
|
||||
"type": "TE-IS",
|
||||
"vertex": "r2"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
38
tests/topotests/isis-topo1/r5/r5_topology.json
Normal file
38
tests/topotests/isis-topo1/r5/r5_topology.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"1": {
|
||||
"level-1": [
|
||||
{
|
||||
"vertex": "r5"
|
||||
},
|
||||
{
|
||||
"metric": "internal",
|
||||
"parent": "0",
|
||||
"type": "IP",
|
||||
"vertex": "10.0.10.0/24"
|
||||
},
|
||||
{
|
||||
"metric": "internal",
|
||||
"parent": "0",
|
||||
"type": "IP",
|
||||
"vertex": "10.0.11.0/24"
|
||||
},
|
||||
{
|
||||
"interface": "r5-eth0",
|
||||
"metric": "10",
|
||||
"next-hop": "r3",
|
||||
"parent": "r5(4)",
|
||||
"type": "TE-IS",
|
||||
"vertex": "r3"
|
||||
},
|
||||
{
|
||||
"interface": "r5-eth1",
|
||||
"metric": "10",
|
||||
"next-hop": "r4",
|
||||
"parent": "r5(4)",
|
||||
"type": "TE-IS",
|
||||
"vertex": "r4"
|
||||
}
|
||||
],
|
||||
"level-2": []
|
||||
}
|
||||
}
|
@ -26,7 +26,10 @@
|
||||
test_isis_topo1.py: Test ISIS topology.
|
||||
"""
|
||||
|
||||
import collections
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import pytest
|
||||
|
||||
@ -114,7 +117,20 @@ def test_isis_convergence():
|
||||
if tgen.routers_have_failure():
|
||||
pytest.skip(tgen.errors)
|
||||
|
||||
topotest.sleep(10, "waiting for ISIS protocol to converge")
|
||||
topotest.sleep(45, "waiting for ISIS protocol to converge")
|
||||
|
||||
# Code to generate the json files.
|
||||
# for rname, router in tgen.routers().iteritems():
|
||||
# open('/tmp/{}_topology.json'.format(rname), 'w').write(
|
||||
# json.dumps(show_isis_topology(router), indent=2, sort_keys=True)
|
||||
# )
|
||||
|
||||
for rname, router in tgen.routers().iteritems():
|
||||
filename = '{0}/{1}/{1}_topology.json'.format(CWD, rname)
|
||||
expected = json.loads(open(filename, 'r').read())
|
||||
actual = show_isis_topology(router)
|
||||
assertmsg = "Router '{}' topology mismatch".format(rname)
|
||||
assert topotest.json_cmp(actual, expected) is None, assertmsg
|
||||
|
||||
|
||||
def test_memory_leak():
|
||||
@ -129,3 +145,135 @@ def test_memory_leak():
|
||||
if __name__ == '__main__':
|
||||
args = ["-s"] + sys.argv[1:]
|
||||
sys.exit(pytest.main(args))
|
||||
|
||||
|
||||
#
|
||||
# Auxiliary functions
|
||||
#
|
||||
|
||||
|
||||
def dict_merge(dct, merge_dct):
|
||||
"""
|
||||
Recursive dict merge. Inspired by :meth:``dict.update()``, instead of
|
||||
updating only top-level keys, dict_merge recurses down into dicts nested
|
||||
to an arbitrary depth, updating keys. The ``merge_dct`` is merged into
|
||||
``dct``.
|
||||
:param dct: dict onto which the merge is executed
|
||||
:param merge_dct: dct merged into dct
|
||||
:return: None
|
||||
|
||||
Source:
|
||||
https://gist.github.com/angstwad/bf22d1822c38a92ec0a9
|
||||
"""
|
||||
for k, v in merge_dct.iteritems():
|
||||
if (k in dct and isinstance(dct[k], dict)
|
||||
and isinstance(merge_dct[k], collections.Mapping)):
|
||||
dict_merge(dct[k], merge_dct[k])
|
||||
else:
|
||||
dct[k] = merge_dct[k]
|
||||
|
||||
|
||||
def parse_topology(lines, level):
|
||||
"""
|
||||
Parse the output of 'show isis topology level-X' into a Python dict.
|
||||
"""
|
||||
areas = {}
|
||||
in_area = False
|
||||
area = None
|
||||
|
||||
for line in lines:
|
||||
if not in_area:
|
||||
area_match = re.match(r"Area (.+):", line)
|
||||
if not area_match:
|
||||
continue
|
||||
|
||||
area = area_match.group(1)
|
||||
areas[area] = {level: []}
|
||||
in_area = True
|
||||
continue
|
||||
|
||||
if re.match(r"IS\-IS paths to", line):
|
||||
continue
|
||||
if re.match(r"Vertex Type Metric Next\-Hop Interface Parent", line):
|
||||
continue
|
||||
|
||||
item_match = re.match(
|
||||
r"([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+)", line)
|
||||
if item_match is not None:
|
||||
areas[area][level].append({
|
||||
'vertex': item_match.group(1),
|
||||
'type': item_match.group(2),
|
||||
'metric': item_match.group(3),
|
||||
'next-hop': item_match.group(4),
|
||||
'interface': item_match.group(5),
|
||||
'parent': item_match.group(6),
|
||||
})
|
||||
continue
|
||||
|
||||
item_match = re.match(r"([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+)", line)
|
||||
if item_match is not None:
|
||||
areas[area][level].append({
|
||||
'vertex': item_match.group(1),
|
||||
'type': item_match.group(2),
|
||||
'metric': item_match.group(3),
|
||||
'parent': item_match.group(4),
|
||||
})
|
||||
continue
|
||||
|
||||
item_match = re.match(r"([^ ]+)", line)
|
||||
if item_match is not None:
|
||||
areas[area][level].append({'vertex': item_match.group(1)})
|
||||
continue
|
||||
|
||||
in_area = False
|
||||
|
||||
return areas
|
||||
|
||||
|
||||
def show_isis_topology(router):
|
||||
"""
|
||||
Get the ISIS topology in a dictionary format.
|
||||
|
||||
Sample:
|
||||
{
|
||||
'area-name': {
|
||||
'level-1': [
|
||||
{
|
||||
'vertex': 'r1'
|
||||
}
|
||||
],
|
||||
'level-2': [
|
||||
{
|
||||
'vertex': '10.0.0.1/24',
|
||||
'type': 'IP',
|
||||
'parent': '0',
|
||||
'metric': 'internal'
|
||||
}
|
||||
]
|
||||
},
|
||||
'area-name-2': {
|
||||
'level-2': [
|
||||
{
|
||||
"interface": "rX-ethY",
|
||||
"metric": "Z",
|
||||
"next-hop": "rA",
|
||||
"parent": "rC(B)",
|
||||
"type": "TE-IS",
|
||||
"vertex": "rD"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
"""
|
||||
l1out = topotest.normalize_text(
|
||||
router.vtysh_cmd('show isis topology level-1')
|
||||
).splitlines()
|
||||
l2out = topotest.normalize_text(
|
||||
router.vtysh_cmd('show isis topology level-2')
|
||||
).splitlines()
|
||||
|
||||
l1 = parse_topology(l1out, 'level-1')
|
||||
l2 = parse_topology(l2out, 'level-2')
|
||||
|
||||
dict_merge(l1, l2)
|
||||
return l1
|
||||
|
Loading…
Reference in New Issue
Block a user