]> git.proxmox.com Git - mirror_frr.git/commitdiff
isis-topo1: test ISIS topology convergence
authorRafael Zalamena <rzalamena@opensourcerouting.org>
Wed, 13 Dec 2017 19:55:28 +0000 (17:55 -0200)
committerDonald Sharp <sharpd@cumulusnetworks.com>
Wed, 28 Nov 2018 01:22:13 +0000 (20:22 -0500)
Add function to parse 'show isis topology' and expect the correct
convergence result.

Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
tests/topotests/isis-topo1/r1/r1_topology.json [new file with mode: 0644]
tests/topotests/isis-topo1/r2/r2_topology.json [new file with mode: 0644]
tests/topotests/isis-topo1/r3/r3_topology.json [new file with mode: 0644]
tests/topotests/isis-topo1/r4/r4_topology.json [new file with mode: 0644]
tests/topotests/isis-topo1/r5/r5_topology.json [new file with mode: 0644]
tests/topotests/isis-topo1/test_isis_topo1.py

diff --git a/tests/topotests/isis-topo1/r1/r1_topology.json b/tests/topotests/isis-topo1/r1/r1_topology.json
new file mode 100644 (file)
index 0000000..86849d1
--- /dev/null
@@ -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"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis-topo1/r2/r2_topology.json b/tests/topotests/isis-topo1/r2/r2_topology.json
new file mode 100644 (file)
index 0000000..a6471b2
--- /dev/null
@@ -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"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis-topo1/r3/r3_topology.json b/tests/topotests/isis-topo1/r3/r3_topology.json
new file mode 100644 (file)
index 0000000..ebc0255
--- /dev/null
@@ -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"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis-topo1/r4/r4_topology.json b/tests/topotests/isis-topo1/r4/r4_topology.json
new file mode 100644 (file)
index 0000000..eaab35b
--- /dev/null
@@ -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"
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/isis-topo1/r5/r5_topology.json b/tests/topotests/isis-topo1/r5/r5_topology.json
new file mode 100644 (file)
index 0000000..5294340
--- /dev/null
@@ -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": []
+  }
+}
index 38308510dfc05f6a56d20e46f6d42d3627d5ea9f..1da2204225090975f636c70e7d0c2ba641c04903 100644 (file)
 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