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:
Rafael Zalamena 2017-12-13 17:55:28 -02:00 committed by Donald Sharp
parent 74bd8dd9ba
commit 67f1e9ed09
6 changed files with 327 additions and 1 deletions

View 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"
}
]
}
}

View 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"
}
]
}
}

View 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"
}
]
}
}

View 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"
}
]
}
}

View 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": []
}
}

View File

@ -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