diff options
Diffstat (limited to 'src/conf_mode/vrrp.py')
-rwxr-xr-x | src/conf_mode/vrrp.py | 203 |
1 files changed, 55 insertions, 148 deletions
diff --git a/src/conf_mode/vrrp.py b/src/conf_mode/vrrp.py index a09e55a2f..3f1b73385 100755 --- a/src/conf_mode/vrrp.py +++ b/src/conf_mode/vrrp.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018 VyOS maintainers and contributors +# Copyright (C) 2018-2020 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -13,145 +13,25 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# import os -import sys -import subprocess -import ipaddress -import jinja2 +from sys import exit +from ipaddress import ip_address, ip_interface, IPv4Interface, IPv6Interface, IPv4Address, IPv6Address +from jinja2 import FileSystemLoader, Environment +from json import dumps +from pathlib import Path import vyos.config import vyos.keepalived +from vyos.defaults import directories as vyos_data_dir from vyos import ConfigError +from vyos.util import call daemon_file = "/etc/default/keepalived" config_file = "/etc/keepalived/keepalived.conf" - -config_tmpl = """ -# Autogenerated by VyOS -# Do not edit this file, all your changes will be lost -# on next commit or reboot - -global_defs { - dynamic_interfaces - script_user root -} - -{% for group in groups -%} - -{% if group.health_check_script -%} -vrrp_script healthcheck_{{ group.name }} { - script "{{ group.health_check_script }}" - interval {{ group.health_check_interval }} - fall {{ group.health_check_count }} - rise 1 - -} -{% endif %} - -vrrp_instance {{ group.name }} { - {% if group.description -%} - # {{ group.description }} - {% endif -%} - - state BACKUP - interface {{ group.interface }} - virtual_router_id {{ group.vrid }} - priority {{ group.priority }} - advert_int {{ group.advertise_interval }} - - {% if group.preempt -%} - preempt_delay {{ group.preempt_delay }} - {% else -%} - nopreempt - {% endif -%} - - {% if group.peer_address -%} - unicast_peer { {{ group.peer_address }} } - {% endif -%} - - {% if group.hello_source -%} - {%- if group.peer_address -%} - unicast_src_ip {{ group.hello_source }} - {%- else -%} - mcast_src_ip {{ group.hello_source }} - {%- endif %} - {% endif -%} - - {% if group.use_vmac and group.peer_address -%} - use_vmac {{group.interface}}v{{group.vrid}} - vmac_xmit_base - {% elif group.use_vmac -%} - use_vmac {{group.interface}}v{{group.vrid}} - {% endif -%} - - {% if group.auth_password -%} - authentication { - auth_pass "{{ group.auth_password }}" - auth_type {{ group.auth_type }} - } - {% endif -%} - - virtual_ipaddress { - {% for addr in group.virtual_addresses -%} - {{ addr }} - {% endfor -%} - } - - {% if group.health_check_script -%} - track_script { - healthcheck_{{ group.name }} - } - {% endif -%} - - {% if group.master_script -%} - notify_master "/usr/libexec/vyos/system/vrrp-script-wrapper.py --state master --group {{ group.name }} --interface {{ group.interface }} {{ group.master_script }}" - {% endif -%} - - {% if group.backup_script -%} - notify_backup "/usr/libexec/vyos/system/vrrp-script-wrapper.py --state backup --group {{ group.name }} --interface {{ group.interface }} {{ group.backup_script }}" - {% endif -%} - - {% if group.fault_script -%} - notify_fault "/usr/libexec/vyos/system/vrrp-script-wrapper.py --state fault --group {{ group.name }} --interface {{ group.interface }} {{ group.fault_script }}" - {% endif -%} - - {% if group.stop_script -%} - notify_stop "/usr/libexec/vyos/system/vrrp-script-wrapper.py --state stop --group {{ group.name }} --interface {{ group.interface }} {{ group.stop_script }}" - {% endif -%} -} - -{% endfor -%} - -{% for sync_group in sync_groups -%} -vrrp_sync_group {{ sync_group.name }} { - group { - {% for member in sync_group.members -%} - {{ member }} - {% endfor -%} - } - - {% if sync_group.conntrack_sync -%} - notify_master "/opt/vyatta/sbin/vyatta-vrrp-conntracksync.sh master {{ sync_group.name }}" - notify_backup "/opt/vyatta/sbin/vyatta-vrrp-conntracksync.sh backup {{ sync_group.name }}" - notify_fault "/opt/vyatta/sbin/vyatta-vrrp-conntracksync.sh fault {{ sync_group.name }}" - {% endif -%} -} - -{% endfor -%} - -""" - -daemon_tmpl = """ -# Autogenerated by VyOS -# Options to pass to keepalived - -# DAEMON_ARGS are appended to the keepalived command-line -DAEMON_ARGS="--snmp" -""" +config_dict_path = "/run/keepalived_config.dict" def get_config(): vrrp_groups = [] @@ -220,7 +100,7 @@ def get_config(): vrrp_groups.append(group) - config.set_level("") + config.set_level("") # Get the sync group used for conntrack-sync conntrack_sync_group = None @@ -238,10 +118,21 @@ def get_config(): if conntrack_sync_group == sync_group_name: sync_group["conntrack_sync"] = True + # add transition script configuration + sync_group["master_script"] = config.return_value("transition-script master") + sync_group["backup_script"] = config.return_value("transition-script backup") + sync_group["fault_script"] = config.return_value("transition-script fault") + sync_group["stop_script"] = config.return_value("transition-script stop") + sync_groups.append(sync_group) + # create a file with dict with proposed configuration + with open("{}.temp".format(config_dict_path), 'w') as dict_file: + dict_file.write(dumps({'vrrp_groups': vrrp_groups, 'sync_groups': sync_groups})) + return (vrrp_groups, sync_groups) + def verify(data): vrrp_groups, sync_groups = data @@ -262,31 +153,31 @@ def verify(data): # XXX: filter on map object is destructive, so we force it to list. # Additionally, filter objects always evaluate to True, empty or not, # so we force them to lists as well. - vaddrs = list(map(lambda i: ipaddress.ip_interface(i), group["virtual_addresses"])) - vaddrs4 = list(filter(lambda x: isinstance(x, ipaddress.IPv4Interface), vaddrs)) - vaddrs6 = list(filter(lambda x: isinstance(x, ipaddress.IPv6Interface), vaddrs)) + vaddrs = list(map(lambda i: ip_interface(i), group["virtual_addresses"])) + vaddrs4 = list(filter(lambda x: isinstance(x, IPv4Interface), vaddrs)) + vaddrs6 = list(filter(lambda x: isinstance(x, IPv6Interface), vaddrs)) if vaddrs4 and vaddrs6: raise ConfigError("VRRP group {0} mixes IPv4 and IPv6 virtual addresses, this is not allowed. Create separate groups for IPv4 and IPv6".format(group["name"])) if vaddrs4: if group["hello_source"]: - hsa = ipaddress.ip_address(group["hello_source"]) - if isinstance(hsa, ipaddress.IPv6Address): + hsa = ip_address(group["hello_source"]) + if isinstance(hsa, IPv6Address): raise ConfigError("VRRP group {0} uses IPv4 but its hello-source-address is IPv6".format(group["name"])) if group["peer_address"]: - pa = ipaddress.ip_address(group["peer_address"]) - if isinstance(pa, ipaddress.IPv6Address): + pa = ip_address(group["peer_address"]) + if isinstance(pa, IPv6Address): raise ConfigError("VRRP group {0} uses IPv4 but its peer-address is IPv6".format(group["name"])) if vaddrs6: if group["hello_source"]: - hsa = ipaddress.ip_address(group["hello_source"]) - if isinstance(hsa, ipaddress.IPv4Address): + hsa = ip_address(group["hello_source"]) + if isinstance(hsa, IPv4Address): raise ConfigError("VRRP group {0} uses IPv6 but its hello-source-address is IPv4".format(group["name"])) if group["peer_address"]: - pa = ipaddress.ip_address(group["peer_address"]) - if isinstance(pa, ipaddress.IPv4Address): + pa = ip_address(group["peer_address"]) + if isinstance(pa, IPv4Address): raise ConfigError("VRRP group {0} uses IPv6 but its peer-address is IPv4".format(group["name"])) # Disallow same VRID on multiple interfaces @@ -308,7 +199,13 @@ def verify(data): if not (m in vrrp_group_names): raise ConfigError("VRRP sync-group {0} refers to VRRP group {1}, but group {1} does not exist".format(sync_group["name"], m)) + def generate(data): + # Prepare Jinja2 template loader from files + tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'vrrp') + fs_loader = FileSystemLoader(tmpl_path) + env = Environment(loader=fs_loader) + vrrp_groups, sync_groups = data # Remove disabled groups from the sync group member lists @@ -318,34 +215,44 @@ def generate(data): if g["disable"]: print("Warning: ignoring disabled VRRP group {0} in sync-group {1}".format(g["name"], sync_group["name"])) # Filter out disabled groups - vrrp_groups = list(filter(lambda x: x["disable"] != True, vrrp_groups)) + vrrp_groups = list(filter(lambda x: x["disable"] is not True, vrrp_groups)) - tmpl = jinja2.Template(config_tmpl) + tmpl = env.get_template('keepalived.conf.tmpl') config_text = tmpl.render({"groups": vrrp_groups, "sync_groups": sync_groups}) with open(config_file, 'w') as f: f.write(config_text) + tmpl = env.get_template('daemon.tmpl') + config_text = tmpl.render() with open(daemon_file, 'w') as f: - f.write(daemon_tmpl) + f.write(config_text) return None + def apply(data): vrrp_groups, sync_groups = data if vrrp_groups: + # safely rename a temporary file with configuration dict + try: + dict_file = Path("{}.temp".format(config_dict_path)) + dict_file.rename(Path(config_dict_path)) + except Exception as err: + print("Unable to rename the file with keepalived config for FIFO pipe: {}".format(err)) + if not vyos.keepalived.vrrp_running(): print("Starting the VRRP process") - ret = subprocess.call("sudo systemctl restart keepalived.service", shell=True) + ret = call("sudo systemctl restart keepalived.service") else: print("Reloading the VRRP process") - ret = subprocess.call("sudo systemctl reload keepalived.service", shell=True) + ret = call("sudo systemctl reload keepalived.service") if ret != 0: raise ConfigError("keepalived failed to start") else: # VRRP is removed in the commit print("Stopping the VRRP process") - subprocess.call("sudo systemctl stop keepalived.service", shell=True) + call("sudo systemctl stop keepalived.service") os.unlink(config_file) return None @@ -359,4 +266,4 @@ if __name__ == '__main__': apply(c) except ConfigError as e: print("VRRP error: {0}".format(str(e))) - sys.exit(1) + exit(1) |