From: Maria Matejka Date: Sat, 20 May 2023 09:51:40 +0000 (+0200) Subject: Python CLI Package: Protocol scaffolding for Kernel, Device, Direct, Babel and RAdv X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=04f96b6705bfc033eb62819de5c72947826399de;p=thirdparty%2Fbird.git Python CLI Package: Protocol scaffolding for Kernel, Device, Direct, Babel and RAdv This choice comes from my own local setup where I use exactly these protocols. Other protocols will be added later. --- diff --git a/python/BIRD/Basic.py b/python/BIRD/Basic.py index 413da117e..01ff0d6f4 100644 --- a/python/BIRD/Basic.py +++ b/python/BIRD/Basic.py @@ -29,3 +29,6 @@ class Code: Welcome = 1 Status = 13 Version = 1000 + ProtocolInfo = 1002 + ProtocolDetails = 1006 + ProtocolListHeader = 2002 diff --git a/python/BIRD/Protocol/Babel.py b/python/BIRD/Protocol/Babel.py new file mode 100644 index 000000000..b2f31d80b --- /dev/null +++ b/python/BIRD/Protocol/Babel.py @@ -0,0 +1,6 @@ +from BIRD.Protocol import Protocol, ProtocolList + +class BabelProtocol(Protocol): + match = "Babel" + +ProtocolList.register(BabelProtocol) diff --git a/python/BIRD/Protocol/Device.py b/python/BIRD/Protocol/Device.py new file mode 100644 index 000000000..521bf6f92 --- /dev/null +++ b/python/BIRD/Protocol/Device.py @@ -0,0 +1,4 @@ +import asyncio + +from BIRD.Basic import Basic +from BIRD.Protocol import Protocol diff --git a/python/BIRD/Protocol/Kernel.py b/python/BIRD/Protocol/Kernel.py new file mode 100644 index 000000000..bbbf41a3b --- /dev/null +++ b/python/BIRD/Protocol/Kernel.py @@ -0,0 +1,16 @@ +from BIRD.Protocol import Protocol, ProtocolList + +class DeviceProtocol(Protocol): + match = "Device" + +ProtocolList.register(DeviceProtocol) + +class DirectProtocol(Protocol): + match = "Direct" + +ProtocolList.register(DirectProtocol) + +class KernelProtocol(Protocol): + match = "Kernel" + +ProtocolList.register(KernelProtocol) diff --git a/python/BIRD/Protocol/RAdv.py b/python/BIRD/Protocol/RAdv.py new file mode 100644 index 000000000..0792d8728 --- /dev/null +++ b/python/BIRD/Protocol/RAdv.py @@ -0,0 +1,6 @@ +from BIRD.Protocol import Protocol, ProtocolList + +class RAdvProtocol(Protocol): + match = "RAdv" + +ProtocolList.register(RAdvProtocol) diff --git a/python/BIRD/Protocol/__init__.py b/python/BIRD/Protocol/__init__.py new file mode 100644 index 000000000..beaf66e3c --- /dev/null +++ b/python/BIRD/Protocol/__init__.py @@ -0,0 +1,78 @@ +import asyncio + +from BIRD.Basic import Basic, BIRDException, Code + +class ProtocolException(Exception): + def __init__(self, msg): + Exception.__init__(self, f"Failed to parse protocol {self.protocol_name}: {msg}") + +class ProtocolListException(Exception): + def __init__(self, msg): + Exception.__init__(self, f"Failed to parse protocol list: {msg}") + +class ProtocolList(Basic): + match = {} +# def __init__(self, **kwargs): +# super().__init__(**kwargs) + + def register(sub): + if sub.match in ProtocolList.match: + raise BIRDException(f"Protocol match {sub.match} already registered for {ProtocolList.match[sub.match]}") + + ProtocolList.match[sub.match] = sub + + async def update(self): + self.data = {} + + await self.bird.cli.open() + data = await self.bird.cli.socket.command("show protocols all") + + # Get header + if data[0]["code"] != Code.ProtocolListHeader: + raise ProtocolListException(f"First line is not protocol list header, got {data[0]}") + + if data[0]["data"].split() != ['Name', 'Proto', 'Table', 'State', 'Since', 'Info']: + raise ProtocolListException(f"Strange protocol list header: {data[0]['data']}") + + data.pop(0) + + for line in data: + if line["code"] == Code.ProtocolInfo: + kwargs = Protocol.parse_info(line["data"]) + + if (name := kwargs["name"]) in self.data: + raise ProtocolListException(f"Duplicate protocol {name}") + + if (m := kwargs["match"]) in self.match: + del kwargs["match"] + kwargs["bird"] = self.bird + self.data[name] = self.match[m](**kwargs) + else: + raise ProtocolListException(f"Unknown protocol kind {m}") + + +class Protocol(Basic): + def __init__(self, name, state, last_change, info, **kwargs): + super().__init__(**kwargs) + + self.name = name + self.state = state + self.last_change = last_change + self.info = info + + def parse_info(data): + s = data.split(maxsplit=5) + [None] + assert(len(s) <= 7) + if len(s) < 6: + raise ProtocolListException(f"Strange protocol info: {data}") + + s.append(None) + s.pop(2) # drop the default table name, it's a BIRD 1 anachronism + return dict(zip( + ["name", "match", "state", "last_change", "info"], + s + )) + +import BIRD.Protocol.Kernel +import BIRD.Protocol.Babel +import BIRD.Protocol.RAdv diff --git a/python/BIRD/__init__.py b/python/BIRD/__init__.py index 2d49ef49f..5511be894 100644 --- a/python/BIRD/__init__.py +++ b/python/BIRD/__init__.py @@ -5,6 +5,7 @@ from datetime import datetime from BIRD.Basic import BIRDException from BIRD.Socket import Socket from BIRD.Status import Status, Version +from BIRD.Protocol import ProtocolList from BIRD.Config import Timestamp, ProtocolConfig, DeviceProtocolConfig @@ -111,8 +112,9 @@ class CLI: class BIRD: def __init__(self, socket=Path("bird.ctl")): self.cli = CLI(socket) - self.version = Version(self) - self.status = Status(self) + self.version = Version(bird=self) + self.status = Status(bird=self) + self.protocols = ProtocolList(bird=self) self.within = False diff --git a/python/test.py b/python/test.py index 31131bfcf..ede00810e 100644 --- a/python/test.py +++ b/python/test.py @@ -9,4 +9,7 @@ async def main(): await b.status.update() print(b.status) + await b.protocols.update() + print(b.protocols) + asyncio.run(main())