Skip to content

Instantly share code, notes, and snippets.

@dgjustice
Created July 16, 2021 00:51
Show Gist options
  • Save dgjustice/84117af73541728ece78cb33c6d37ecb to your computer and use it in GitHub Desktop.
Save dgjustice/84117af73541728ece78cb33c6d37ecb to your computer and use it in GitHub Desktop.
Cannonical interface sorting
from itertools import groupby
import re
import typing as t
WEIGHTS: t.Dict[str, int] = {".": 10, "/": 20}
def split_interface_tuple(interface: str) -> t.Tuple[str, ...]:
"""Crappy parser-combinator hacky-hack"""
head = interface.rstrip(r"/\0123456789. ")
rest = interface[len(head) :].lstrip()
idx = 0
# we mutate tail's reference, so mypy needs help
tail: t.Tuple[str, ...] = (head,)
while idx < len(rest):
part = ""
while idx < len(rest) and re.match(r"[a-zA-Z\-]", rest[idx]):
part += rest[idx]
idx += 1
if part:
tail = (*tail, part)
part = ""
while idx < len(rest) and re.match(r"[0-9]", rest[idx]):
part += rest[idx]
idx += 1
if part:
tail = (*tail, part)
part = ""
while idx < len(rest) and re.match(r"[./]", rest[idx]):
part += rest[idx]
idx += 1
if part:
tail = (*tail, part)
return tail
def insert_nodes(node, values) -> None:
"""Recursively updates a tree from a list of values.
This function mutates the node dict in place."""
if not values:
return
val = values[0]
weight = WEIGHTS.get(val, 30)
if (val, weight) not in node:
node[(val, weight)] = {}
insert_nodes(node[(val, weight)], values[1:])
def iter_tree(node, parents=[]):
"""Walk a tree of interface name parts.
Weights are assigned based on domain logic to produce a
'cannonical' ordering of names. Child nodes are visited
first by priority of weight, then lexicographically."""
for _, items in groupby(node.keys(), lambda t: t[1]):
for item in sorted(items, key=lambda v: v[0]):
if not node[item]:
yield "".join(parents + [item[0]])
continue
parents.append(item[0])
yield from iter_tree(node[item], list(parents))
parents.pop()
if __name__ == "__main__":
test_data = [
"Fa0/0/2",
"Fa0/0/1",
"Fa1/0/1.100",
"Fa1/0.100",
"Fa0/0/1.101",
"Fa0/0/1.100",
"Fa0/0/1.100",
"Fa0/0/2.200",
"Gi0/1",
"Gi0/0/1",
"Gi0/0/2",
"Gi1/0/1",
"Te1/0/1",
"Gi1/0/2",
"Vlan42",
"loopback99",
"Port-channel75",
]
root: t.Dict[str, t.Any] = {}
for ifname in test_data:
insert_nodes(root, split_interface_tuple(ifname))
for thing in iter_tree(root):
print(thing)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment