diff options
| author | Daniil Baturin <daniil@vyos.io> | 2025-10-08 17:04:19 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-10-08 17:04:19 +0100 |
| commit | 1c181499a3af4b6c170d3fea2b96af58bb0a34a7 (patch) | |
| tree | 44498595ab266f4e569b50db786fc42e90a6b72b /src/conf_mode/protocols_failover.py | |
| parent | fc178aaf00f7a9dc4589567ae3e17cb9718d9ad3 (diff) | |
| parent | 931e5b5712b4fa8b13027291eb21881c40b8a051 (diff) | |
| download | vyos-1x-1c181499a3af4b6c170d3fea2b96af58bb0a34a7.tar.gz vyos-1x-1c181499a3af4b6c170d3fea2b96af58bb0a34a7.zip | |
Merge pull request #4749 from hedrok/T7803-failover-vrf
T7803: Make failover route vrf-aware
Diffstat (limited to 'src/conf_mode/protocols_failover.py')
| -rwxr-xr-x | src/conf_mode/protocols_failover.py | 83 |
1 files changed, 72 insertions, 11 deletions
diff --git a/src/conf_mode/protocols_failover.py b/src/conf_mode/protocols_failover.py index 9b50cc1ed..956c060f4 100755 --- a/src/conf_mode/protocols_failover.py +++ b/src/conf_mode/protocols_failover.py @@ -15,12 +15,15 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import json +import os from pathlib import Path +from sys import argv from vyos.config import Config from vyos.template import render from vyos.utils.process import call +from vyos.utils.process import is_systemd_service_running from vyos import ConfigError from vyos import airbag @@ -28,9 +31,24 @@ airbag.enable() service_name = 'vyos-failover' -service_conf = Path(f'/run/{service_name}.conf') +service_conf_dir = Path(f'/run/{service_name}.conf.d/') systemd_service = '/run/systemd/system/vyos-failover.service' -rt_proto_failover = '/etc/iproute2/rt_protos.d/failover.conf' +rt_proto_failover = Path('/etc/iproute2/rt_protos.d/failover.conf') + + +def get_vrf_name(): + if argv and len(argv) > 1: + return argv[1] + return None + + +def get_service_conf_path(): + vrf_name = get_vrf_name() + if vrf_name: + filename = f'vrf-{vrf_name}.conf' + else: + filename = 'default.conf' + return service_conf_dir / filename def get_config(config=None): @@ -39,7 +57,14 @@ def get_config(config=None): else: conf = Config() - base = ['protocols', 'failover'] + vrf_name = get_vrf_name() + if vrf_name: + base = ['vrf', 'name', vrf_name] + else: + base = [] + + base += ['protocols', 'failover'] + failover = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) @@ -47,6 +72,9 @@ def get_config(config=None): if failover.get('route') is not None: failover = conf.merge_defaults(failover, recursive=True) + if failover: + failover['vrf_context'] = vrf_name + return failover def verify(failover): @@ -75,33 +103,66 @@ def verify(failover): if check_type == 'tcp' and 'port' not in next_hop_config['check']: raise ConfigError(f'Check port for next-hop "{next_hop}" and type TCP is mandatory!') + errors = { + 'icmp': {}, + 'tcp': { + 'interface': 'Check target "interface" option does nothing for type TCP. Use "vrf" if needed', + }, + 'arp': { + 'vrf': 'Check target "vrf" option is incompatible with type ARP, use "interface" option if needed', + }, + } + + for target, target_config in next_hop_config['check']['target'].items(): + for key, msg in errors[check_type].items(): + if key in target_config: + raise ConfigError(msg) + return None + def generate(failover): + service_conf = get_service_conf_path() if not failover: service_conf.unlink(missing_ok=True) + try: + os.rmdir(service_conf_dir) + # Ignore if directory doesn't exist + # or not empty (probably configs for other VRFs are there) + except (FileNotFoundError, OSError): + pass return None # Add own rt_proto 'failover' # Helps to detect all own routes 'proto failover' - with open(rt_proto_failover, 'w') as f: - f.write('111 failover\n') + rt_proto_failover.write_text('111 failover\n') + + service_conf_dir.mkdir(exist_ok=True) # Write configuration file conf_json = json.dumps(failover, indent=4) service_conf.write_text(conf_json) - render(systemd_service, 'protocols/systemd_vyos_failover_service.j2', failover) + render( + systemd_service, + 'protocols/systemd_vyos_failover_service.j2', + {'config_dir': str(service_conf_dir)}, + ) return None def apply(failover): - if not failover: + # If directory is removed - we can stop the service + if not service_conf_dir.is_dir(): call(f'systemctl stop {service_name}.service') - call('ip route flush protocol failover') - else: call('systemctl daemon-reload') - call(f'systemctl restart {service_name}.service') - call(f'ip route flush protocol failover') + # Otherwise even if `failover` is False, service is + # still needed for other VRFs. + else: + # Daemon watches for configuration updates, so we need only + # to start it if it is not started yet + if not is_systemd_service_running(service_name): + call('systemctl daemon-reload') + call(f'systemctl start {service_name}.service') return None |
