0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-23 02:01:55 +00:00
bird/python/BIRD/ShowRoute.py
2024-07-19 19:28:19 +02:00

156 lines
4.4 KiB
Python

import re
class ParserException(Exception):
pass
def subparser(base):
def decorator(cls):
base.subparsers = { **base.subparsers, cls.entryRegex: cls }
return cls
return decorator
class CLIParser:
subparsers = {}
def __init__(self, groups=None, parent=None):
self.result = {}
self.parent = parent
self.cur = None
if parent is None:
assert(groups is None)
return
self.enter(groups)
def parse(self, line: str):
assert(self.cur == None)
if line is not None:
for k,v in self.subparsers.items():
if m := k.match(line):
self.cur = (c := v(groups=m.groups(), parent=self))
while c.cur is not None:
c = c.cur
return c
elif self.parent is None:
return self
try:
self.exit()
self.parent.cur = None
return self.parent.parse(line)
except Exception as e:
print(f"Failed to parse line: {line}")
raise e
def exit(self):
raise ParserException(f"Failed to match line to all regexes")
@subparser(CLIParser)
class VersionParser(CLIParser):
entryRegex = re.compile("BIRD ([0-9a-z._-]+) ready.")
def enter(self, groups):
self.version ,= groups
def exit(self):
if "version" in self.parent.result:
raise ParserException(f"Duplicate version line")
self.parent.result["version"] = self.version
class ShowRouteParser(CLIParser):
def __init__(self):
super().__init__()
self.result["tables"] = {}
@subparser(ShowRouteParser)
class NothingParser(CLIParser):
entryRegex = re.compile("^$")
def enter(self, _):
pass
def exit(self):
pass
@subparser(ShowRouteParser)
class TableParser(CLIParser):
entryRegex = re.compile("^Table (.*):$")
def enter(self, groups):
self.name ,= groups
self.result["networks"] = {}
def exit(self):
if self.name in self.parent.result["tables"]:
raise ParserException(f"Duplicate table name")
self.parent.result["tables"][self.name] = self.result
@subparser(TableParser)
class NetworkParser(CLIParser):
entryRegex = re.compile("([0-9a-f:.]+/[0-9]+)(\\s+.*)")
def enter(self, groups):
self.network, rest = groups
self.result["routes"] = []
self.parse(rest)
def exit(self):
self.parent.result["networks"][self.network] = self.result
@subparser(NetworkParser)
class RouteParser(CLIParser):
entryRegex = re.compile("\\s+(unicast|unreachable) \\[(.*) ((?:[0-9]{2}:){2}[0-9]{2}\\.[0-9]{3})\\] (.*)")
def enter(self, groups):
self.result = {
k: v for k,v in zip(("dest", "proto", "when", "args"), groups)
}
if self.result["dest"] == "unicast":
self.result["nexthop"] = []
def exit(self):
self.parent.result["routes"].append(self.result)
@subparser(RouteParser)
class NextHopParser(CLIParser):
entryRegex = re.compile("\\s+via ([0-9a-f:.]+) on (.*)")
def enter(self, groups):
self.result = {
k: v for k,v in zip(("nexthop", "iface"), groups)
}
def exit(self):
self.parent.result["nexthop"].append(self.result)
@subparser(RouteParser)
class AttributeParser(CLIParser):
entryRegex = re.compile("\\s+([a-zA-Z_.0-9]+): (.*)$")
def enter(self, groups):
self.key, self.value = groups
def exit(self):
if self.key in self.parent.result:
raise ParserException(f"Duplicate key {self.key} in route")
self.parent.result[self.key] = self.value
@subparser(RouteParser)
class InternalRouteHandlingValuesParser(CLIParser):
entryRegex = re.compile("\\s+Internal route handling values: (\\d+)L (\\d+)G (\\d+)S id (\\d+)$")
def enter(self, groups):
self.result = {
k: v for k,v in zip(("!_l", "!_g", "!_s", "!_id"), groups)
}
def exit(self):
for k,v in self.result.items():
if k in self.parent.result:
raise ParserException(f"Duplicate internal value {k} in route")
self.parent.result[k] = v
def parse(data: str):
parser = ShowRouteParser()
for line in data.split("\n"):
parser = parser.parse(line)
parser = parser.parse(None)
return parser.result