summaryrefslogtreecommitdiff
path: root/src/conf_mode/vrrp.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/conf_mode/vrrp.py')
-rwxr-xr-xsrc/conf_mode/vrrp.py203
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)