From c8d75f88f078c8c2c2a1f6a9a62c937424279d4b Mon Sep 17 00:00:00 2001 From: Christian Breunig Date: Wed, 7 Oct 2026 19:59:13 +0200 Subject: frrender: T9368: reconcile FRR after a DHCP lease without the commit socket Reconciling the FRR configuration after a DHCP lease event was requested from the configuration daemon over the socket the commit pipeline uses. The daemon serves one client at a time and assumes it is the running commit, so the request either landed in the middle of the handshake which hands over the configuration, or kept the daemon busy past the 10ms a commit waits for it. Either way the commit fell back to running the conf-mode scripts directly. Those never render FRR, only the daemon does, so the commit succeeded while FRR kept the configuration rendered before it, until the next lease event. A lease event now renders FRR in its own process, and a commit which could not use the daemon renders FRR itself instead of skipping the reload. Both take the same lock. A commit post-hook would remove the second case, for a full render on every commit. --- python/vyos/frrender.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'python') diff --git a/python/vyos/frrender.py b/python/vyos/frrender.py index eac03e4d3..371ea95b5 100644 --- a/python/vyos/frrender.py +++ b/python/vyos/frrender.py @@ -21,8 +21,10 @@ frr-reload.py, if the configuration has no errors. Will fail early if the rendered configuration has any errors. """ +import fcntl import os +from contextlib import contextmanager from copy import deepcopy from time import sleep @@ -33,6 +35,7 @@ from vyos.configdict import get_pppoe_interfaces from vyos.defaults import frr_debug_enable from vyos.utils.dict import dict_search from vyos.utils.dict import dict_set_nested +from vyos.utils.file import read_file from vyos.utils.file import write_file from vyos.utils.process import rc_cmd from vyos.template import get_dhcp_router @@ -44,6 +47,21 @@ def debug(message): return print(message) +frr_config_file: str = '/run/frr/config/vyos.frr.conf' +# Configuration of the last successful reload, for consumers which have no +# cached configuration of their own +frr_applied_config_file: str = '/run/frr/config/vyos.frr.applied.conf' +frr_render_lock_file: str = '/run/vyos-frr-render.lock' + +@contextmanager +def frr_render_lock(): + """Serialize everything which renders FRR. The rendered configuration is a + single file handed to frr-reload.py, so a second renderer would rewrite it + while FRR is being reloaded from it.""" + with open(frr_render_lock_file, 'w') as lock_file: + fcntl.lockf(lock_file, fcntl.LOCK_EX) + yield + ERROR_RELOAD_TEST: str = 'The system encountered an error while rendering the ' \ 'new routing daemon configuration. To ensure network stability and avoid ' \ 'potential connectivity disruptions, the configuration was not applied!' @@ -747,7 +765,7 @@ class FRRender: cached_config_dict = {} cached_dhcp_gateways = {} def __init__(self): - self._frr_conf = '/run/frr/config/vyos.frr.conf' + self._frr_conf = frr_config_file def generate(self, config_dict) -> None: """ @@ -935,6 +953,8 @@ class FRRender: if count >= count_max: raise ConfigError(emsg) + write_file(frr_applied_config_file, read_file(self._frr_conf)) + # frr-reload.py --reload has already saved the configuration to # /etc/frr/frr.conf (bind-mounted from /run/frr/config/frr.conf): it # does so whenever it is not run with --daemon. T3217 added a second -- cgit v1.2.3