summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/high-availability/keepalived.conf.tmpl157
-rw-r--r--interface-definitions/high-availability.xml.in12
-rw-r--r--interface-definitions/vrrp.xml.in257
-rwxr-xr-xsmoketest/scripts/cli/test_ha_virtual_server.py146
-rwxr-xr-xsrc/conf_mode/high-availability.py (renamed from src/conf_mode/vrrp.py)63
5 files changed, 351 insertions, 284 deletions
diff --git a/data/templates/high-availability/keepalived.conf.tmpl b/data/templates/high-availability/keepalived.conf.tmpl
new file mode 100644
index 000000000..817c65ff0
--- /dev/null
+++ b/data/templates/high-availability/keepalived.conf.tmpl
@@ -0,0 +1,157 @@
+# 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
+ notify_fifo /run/keepalived/keepalived_notify_fifo
+ notify_fifo_script /usr/libexec/vyos/system/keepalived-fifo.py
+}
+
+{% if vrrp is defined and vrrp.group is defined and vrrp.group is not none %}
+{% for name, group_config in vrrp.group.items() if group_config.disable is not defined %}
+{% if group_config.health_check is defined and group_config.health_check.script is defined and group_config.health_check.script is not none %}
+vrrp_script healthcheck_{{ name }} {
+ script "{{ group_config.health_check.script }}"
+ interval {{ group_config.health_check.interval }}
+ fall {{ group_config.health_check.failure_count }}
+ rise 1
+}
+{% endif %}
+vrrp_instance {{ name }} {
+{% if group_config.description is defined and group_config.description is not none %}
+ # {{ group_config.description }}
+{% endif %}
+ state BACKUP
+ interface {{ group_config.interface }}
+ virtual_router_id {{ group_config.vrid }}
+ priority {{ group_config.priority }}
+ advert_int {{ group_config.advertise_interval }}
+{% if group_config.no_preempt is not defined and group_config.preempt_delay is defined and group_config.preempt_delay is not none %}
+ preempt_delay {{ group_config.preempt_delay }}
+{% elif group_config.no_preempt is defined %}
+ nopreempt
+{% endif %}
+{% if group_config.peer_address is defined and group_config.peer_address is not none %}
+ unicast_peer { {{ group_config.peer_address }} }
+{% endif %}
+{% if group_config.hello_source_address is defined and group_config.hello_source_address is not none %}
+{% if group_config.peer_address is defined and group_config.peer_address is not none %}
+ unicast_src_ip {{ group_config.hello_source_address }}
+{% else %}
+ mcast_src_ip {{ group_config.hello_source_address }}
+{% endif %}
+{% endif %}
+{% if group_config.rfc3768_compatibility is defined and group_config.peer_address is defined %}
+ use_vmac {{ group_config.interface }}v{{ group_config.vrid }}
+ vmac_xmit_base
+{% elif group_config.rfc3768_compatibility is defined %}
+ use_vmac {{ group_config.interface }}v{{ group_config.vrid }}
+{% endif %}
+{% if group_config.authentication is defined and group_config.authentication is not none %}
+ authentication {
+ auth_pass "{{ group_config.authentication.password }}"
+{% if group_config.authentication.type == 'plaintext-password' %}
+ auth_type PASS
+{% else %}
+ auth_type {{ group_config.authentication.type | upper }}
+{% endif %}
+ }
+{% endif %}
+{% if group_config.address is defined and group_config.address is not none %}
+ virtual_ipaddress {
+{% for addr in group_config.address %}
+ {{ addr }}
+{% endfor %}
+ }
+{% endif %}
+{% if group_config.excluded_address is defined and group_config.excluded_address is not none %}
+ virtual_ipaddress_excluded {
+{% for addr in group_config.excluded_address %}
+ {{ addr }}
+{% endfor %}
+ }
+{% endif %}
+{% if group_config.health_check is defined and group_config.health_check.script is defined and group_config.health_check.script is not none %}
+ track_script {
+ healthcheck_{{ name }}
+ }
+{% endif %}
+}
+{% endfor %}
+{% endif %}
+
+{% if vrrp is defined and vrrp.sync_group is defined and vrrp.sync_group is not none %}
+{% for name, sync_group_config in vrrp.sync_group.items() if sync_group_config.disable is not defined %}
+vrrp_sync_group {{ name }} {
+ group {
+{% if sync_group_config.member is defined and sync_group_config.member is not none %}
+{% for member in sync_group_config.member %}
+ {{ member }}
+{% endfor %}
+{% endif %}
+ }
+
+{# Health-check scripts should be in section sync-group if member is part of the sync-group T4081 #}
+{% for name, group_config in vrrp.group.items() if group_config.disable is not defined %}
+{% if group_config.health_check is defined and group_config.health_check.script is defined and group_config.health_check.script is not none and name in sync_group_config.member %}
+ track_script {
+ healthcheck_{{ name }}
+ }
+{% endif %}
+{% endfor %}
+{% if vrrp.conntrack_sync_group is defined and vrrp.conntrack_sync_group == name %}
+{% set vyos_helper = "/usr/libexec/vyos/vyos-vrrp-conntracksync.sh" %}
+ notify_master "{{ vyos_helper }} master {{ name }}"
+ notify_backup "{{ vyos_helper }} backup {{ name }}"
+ notify_fault "{{ vyos_helper }} fault {{ name }}"
+{% endif %}
+}
+{% endfor %}
+{% endif %}
+
+# Virtual-server configuration
+{% if virtual_server is defined and virtual_server is not none %}
+{% for vserver, vserver_config in virtual_server.items() %}
+virtual_server {{ vserver }} {{ vserver_config.port }} {
+ delay_loop {{ vserver_config.delay_loop }}
+{% if vserver_config.algorithm == 'round-robin' %}
+ lb_algo rr
+{% elif vserver_config.algorithm == 'weighted-round-robin' %}
+ lb_algo wrr
+{% elif vserver_config.algorithm == 'least-connection' %}
+ lb_algo lc
+{% elif vserver_config.algorithm == 'weighted-least-connection' %}
+ lb_algo wlc
+{% elif vserver_config.algorithm == 'source-hashing' %}
+ lb_algo sh
+{% elif vserver_config.algorithm == 'destination-hashing' %}
+ lb_algo dh
+{% elif vserver_config.algorithm == 'locality-based-least-connection' %}
+ lb_algo lblc
+{% endif %}
+{% if vserver_config.forward_method == "nat" %}
+ lb_kind NAT
+{% elif vserver_config.forward_method == "direct" %}
+ lb_kind DR
+{% elif vserver_config.forward_method == "tunnel" %}
+ lb_kind TUN
+{% endif %}
+ persistence_timeout {{ vserver_config.persistence_timeout }}
+ protocol {{ vserver_config.protocol | upper }}
+{% if vserver_config.real_server is defined and vserver_config.real_server is not none %}
+{% for rserver, rserver_config in vserver_config.real_server.items() %}
+ real_server {{ rserver }} {{ rserver_config.port }} {
+ weight 1
+ {{ vserver_config.protocol | upper }}_CHECK {
+{% if rserver_config.connection_timeout is defined and rserver_config.connection_timeout is not none %}
+ connect_timeout {{ rserver_config.connection_timeout }}
+{% endif %}
+ }
+ }
+{% endfor %}
+{% endif %}
+}
+{% endfor %}
+{% endif %}
diff --git a/interface-definitions/high-availability.xml.in b/interface-definitions/high-availability.xml.in
index 42cdceed1..f46343c76 100644
--- a/interface-definitions/high-availability.xml.in
+++ b/interface-definitions/high-availability.xml.in
@@ -312,7 +312,7 @@
</leafNode>
<leafNode name="forward-method">
<properties>
- <help>Forwarding method (default - NAT)</help>
+ <help>Forwarding method (default: NAT)</help>
<completionHelp>
<list>direct nat tunnel</list>
</completionHelp>
@@ -340,7 +340,7 @@
<help>Timeout for persistent connections</help>
<valueHelp>
<format>u32:1-86400</format>
- <description>Timeout for persistent connections (default 300)</description>
+ <description>Timeout for persistent connections (default: 300)</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-86400"/>
@@ -350,17 +350,17 @@
</leafNode>
<leafNode name="protocol">
<properties>
- <help>Protocol for port checks (default TCP)</help>
+ <help>Protocol for port checks (default: TCP)</help>
<completionHelp>
<list>tcp udp</list>
</completionHelp>
<valueHelp>
<format>tcp</format>
- <description>Protocol TCP</description>
+ <description>TCP</description>
</valueHelp>
<valueHelp>
<format>udp</format>
- <description>Protocol UDP</description>
+ <description>UDP</description>
</valueHelp>
<constraint>
<regex>^(tcp|udp)$</regex>
@@ -376,7 +376,7 @@
#include <include/port-number.xml.i>
<leafNode name="connection-timeout">
<properties>
- <help>Connection timeout to remote server</help>
+ <help>Server connection timeout</help>
<valueHelp>
<format>u32:1-86400</format>
<description>Connection timeout to remote server</description>
diff --git a/interface-definitions/vrrp.xml.in b/interface-definitions/vrrp.xml.in
deleted file mode 100644
index 53d79caac..000000000
--- a/interface-definitions/vrrp.xml.in
+++ /dev/null
@@ -1,257 +0,0 @@
-<?xml version="1.0"?>
-<interfaceDefinition>
- <node name="high-availability">
- <properties>
- <help>High availability settings</help>
- </properties>
- <children>
- <node name="vrrp" owner="${vyos_conf_scripts_dir}/vrrp.py">
- <properties>
- <priority>800</priority> <!-- after all interfaces and conntrack-sync -->
- <help>Virtual Router Redundancy Protocol settings</help>
- </properties>
- <children>
- <tagNode name="group">
- <properties>
- <help>VRRP group</help>
- </properties>
- <children>
- #include <include/generic-interface-broadcast.xml.i>
- <leafNode name="advertise-interval">
- <properties>
- <help>Advertise interval</help>
- <valueHelp>
- <format>u32:1-255</format>
- <description>Advertise interval in seconds (default: 1)</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-255"/>
- </constraint>
- </properties>
- <defaultValue>1</defaultValue>
- </leafNode>
- <node name="authentication">
- <properties>
- <help>VRRP authentication</help>
- </properties>
- <children>
- <leafNode name="password">
- <properties>
- <help>VRRP password</help>
- <valueHelp>
- <format>txt</format>
- <description>Password string (up to 8 characters)</description>
- </valueHelp>
- <constraint>
- <regex>.{1,8}</regex>
- </constraint>
- <constraintErrorMessage>Password must not be longer than 8 characters</constraintErrorMessage>
- </properties>
- </leafNode>
- <leafNode name="type">
- <properties>
- <help>Authentication type</help>
- <completionHelp>
- <list>plaintext-password ah</list>
- </completionHelp>
- <valueHelp>
- <format>plaintext-password</format>
- <description>Simple password string</description>
- </valueHelp>
- <valueHelp>
- <format>ah</format>
- <description>AH - IPSEC (not recommended)</description>
- </valueHelp>
- <constraint>
- <regex>^(plaintext-password|ah)$</regex>
- </constraint>
- <constraintErrorMessage>Authentication type must be plaintext-password or ah</constraintErrorMessage>
- </properties>
- </leafNode>
- </children>
- </node>
- #include <include/generic-description.xml.i>
- #include <include/generic-disable-node.xml.i>
- <node name="health-check">
- <properties>
- <help>Health check script</help>
- </properties>
- <children>
- <leafNode name="failure-count">
- <properties>
- <help>Health check failure count required for transition to fault (default: 3)</help>
- <constraint>
- <validator name="numeric" argument="--positive" />
- </constraint>
- </properties>
- <defaultValue>3</defaultValue>
- </leafNode>
- <leafNode name="interval">
- <properties>
- <help>Health check execution interval in seconds (default: 60)</help>
- <constraint>
- <validator name="numeric" argument="--positive"/>
- </constraint>
- </properties>
- <defaultValue>60</defaultValue>
- </leafNode>
- <leafNode name="script">
- <properties>
- <help>Health check script file</help>
- <constraint>
- <validator name="script"/>
- </constraint>
- </properties>
- </leafNode>
- </children>
- </node>
- <leafNode name="hello-source-address">
- <properties>
- <help>VRRP hello source address</help>
- <valueHelp>
- <format>ipv4</format>
- <description>IPv4 hello source address</description>
- </valueHelp>
- <valueHelp>
- <format>ipv6</format>
- <description>IPv6 hello source address</description>
- </valueHelp>
- <constraint>
- <validator name="ipv4-address"/>
- <validator name="ipv6-address"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="peer-address">
- <properties>
- <help>Unicast VRRP peer address</help>
- <valueHelp>
- <format>ipv4</format>
- <description>IPv4 unicast peer address</description>
- </valueHelp>
- <valueHelp>
- <format>ipv6</format>
- <description>IPv6 unicast peer address</description>
- </valueHelp>
- <constraint>
- <validator name="ipv4-address"/>
- <validator name="ipv6-address"/>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="no-preempt">
- <properties>
- <valueless/>
- <help>Disable master preemption</help>
- </properties>
- </leafNode>
- <leafNode name="preempt-delay">
- <properties>
- <help>Preempt delay (in seconds)</help>
- <valueHelp>
- <format>u32:0-1000</format>
- <description>preempt delay</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-1000"/>
- </constraint>
- </properties>
- <defaultValue>0</defaultValue>
- </leafNode>
- <leafNode name="priority">
- <properties>
- <help>Router priority (default: 100)</help>
- <valueHelp>
- <format>u32:1-255</format>
- <description>Router priority</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-255"/>
- </constraint>
- </properties>
- <defaultValue>100</defaultValue>
- </leafNode>
- <leafNode name="rfc3768-compatibility">
- <properties>
- <help>Use VRRP virtual MAC address as per RFC3768</help>
- <valueless/>
- </properties>
- </leafNode>
- #include <include/vrrp-transition-script.xml.i>
- <leafNode name="address">
- <properties>
- <help>Virtual IP address</help>
- <valueHelp>
- <format>ipv4</format>
- <description>IPv4 virtual address</description>
- </valueHelp>
- <valueHelp>
- <format>ipv6</format>
- <description>IPv6 virtual address</description>
- </valueHelp>
- <constraint>
- <validator name="ipv4-host"/>
- <validator name="ipv6-host"/>
- </constraint>
- <multi/>
- </properties>
- </leafNode>
- <leafNode name="excluded-address">
- <properties>
- <help>Virtual address (If you need additional IPv4 and IPv6 in same group)</help>
- <valueHelp>
- <format>ipv4</format>
- <description>IP address</description>
- </valueHelp>
- <valueHelp>
- <format>ipv6</format>
- <description>IPv6 address</description>
- </valueHelp>
- <multi/>
- <constraint>
- <validator name="ipv4-host"/>
- <validator name="ipv6-host"/>
- </constraint>
- <constraintErrorMessage>Virtual address must be a valid IPv4 or IPv6 address with prefix length (e.g. 192.0.2.3/24 or 2001:db8:ff::10/64)</constraintErrorMessage>
- </properties>
- </leafNode>
- <leafNode name="vrid">
- <properties>
- <help>Virtual router identifier</help>
- <valueHelp>
- <format>u32:1-255</format>
- <description>Virtual router identifier</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-255"/>
- </constraint>
- </properties>
- </leafNode>
- </children>
- </tagNode>
- <tagNode name="sync-group">
- <properties>
- <help>VRRP sync group</help>
- </properties>
- <children>
- <leafNode name="member">
- <properties>
- <multi/>
- <help>Sync group member</help>
- <valueHelp>
- <format>txt</format>
- <description>VRRP group name</description>
- </valueHelp>
- <completionHelp>
- <path>high-availability vrrp group</path>
- </completionHelp>
- </properties>
- </leafNode>
- #include <include/vrrp-transition-script.xml.i>
- </children>
- </tagNode>
- </children>
- </node>
- </children>
- </node>
-</interfaceDefinition>
diff --git a/smoketest/scripts/cli/test_ha_virtual_server.py b/smoketest/scripts/cli/test_ha_virtual_server.py
new file mode 100755
index 000000000..e3a91283e
--- /dev/null
+++ b/smoketest/scripts/cli/test_ha_virtual_server.py
@@ -0,0 +1,146 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021-2022 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
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# 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 unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.configsession import ConfigSessionError
+from vyos.ifconfig.vrrp import VRRP
+from vyos.util import cmd
+from vyos.util import process_named_running
+from vyos.util import read_file
+from vyos.template import inc_ip
+
+PROCESS_NAME = 'keepalived'
+KEEPALIVED_CONF = VRRP.location['config']
+base_path = ['high-availability']
+vrrp_interface = 'eth1'
+
+class TestHAVirtualServer(VyOSUnitTestSHIM.TestCase):
+ def tearDown(self):
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+ self.cli_delete(['interfaces', 'ethernet', vrrp_interface, 'address'])
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ # Process must be terminated after deleting the config
+ self.assertFalse(process_named_running(PROCESS_NAME))
+
+ def test_01_ha_virtual_server(self):
+ algo = 'least-connection'
+ delay = '10'
+ method = 'nat'
+ persistence_timeout = '600'
+ vip = '203.0.113.111'
+ vport = '2222'
+ rservers = ['192.0.2.21', '192.0.2.22', '192.0.2.23']
+ rport = '22'
+ proto = 'tcp'
+ connection_timeout = '30'
+
+ vserver_base = base_path + ['virtual-server']
+
+ self.cli_set(vserver_base + [vip, 'algorithm', algo])
+ self.cli_set(vserver_base + [vip, 'delay-loop', delay])
+ self.cli_set(vserver_base + [vip, 'forward-method', method])
+ self.cli_set(vserver_base + [vip, 'persistence-timeout', persistence_timeout])
+ self.cli_set(vserver_base + [vip, 'port', vport])
+ self.cli_set(vserver_base + [vip, 'protocol', proto])
+ for rs in rservers:
+ self.cli_set(vserver_base + [vip, 'real-server', rs, 'connection-timeout', connection_timeout])
+ self.cli_set(vserver_base + [vip, 'real-server', rs, 'port', rport])
+
+ # commit changes
+ self.cli_commit()
+
+ config = read_file(KEEPALIVED_CONF)
+
+ self.assertIn(f'delay_loop {delay}', config)
+ self.assertIn(f'lb_algo lc', config)
+ self.assertIn(f'lb_kind {method.upper()}', config)
+ self.assertIn(f'persistence_timeout {persistence_timeout}', config)
+ self.assertIn(f'protocol {proto.upper()}', config)
+ for rs in rservers:
+ self.assertIn(f'real_server {rs} {rport}', config)
+ self.assertIn(f'{proto.upper()}_CHECK', config)
+ self.assertIn(f'connect_timeout {connection_timeout}', config)
+
+ def test_02_ha_virtual_server_and_vrrp(self):
+ algo = 'least-connection'
+ delay = '15'
+ method = 'nat'
+ persistence_timeout = '300'
+ vip = '203.0.113.222'
+ vport = '22322'
+ rservers = ['192.0.2.11', '192.0.2.12']
+ rport = '222'
+ proto = 'tcp'
+ connection_timeout = '23'
+ group = 'VyOS'
+ vrid = '99'
+
+ vrrp_base = base_path + ['vrrp', 'group']
+ vserver_base = base_path + ['virtual-server']
+
+ self.cli_set(['interfaces', 'ethernet', vrrp_interface, 'address', '203.0.113.10/24'])
+
+ # VRRP config
+ self.cli_set(vrrp_base + [group, 'description', group])
+ self.cli_set(vrrp_base + [group, 'interface', vrrp_interface])
+ self.cli_set(vrrp_base + [group, 'address', vip + '/24'])
+ self.cli_set(vrrp_base + [group, 'vrid', vrid])
+
+ # Virtual-server config
+ self.cli_set(vserver_base + [vip, 'algorithm', algo])
+ self.cli_set(vserver_base + [vip, 'delay-loop', delay])
+ self.cli_set(vserver_base + [vip, 'forward-method', method])
+ self.cli_set(vserver_base + [vip, 'persistence-timeout', persistence_timeout])
+ self.cli_set(vserver_base + [vip, 'port', vport])
+ self.cli_set(vserver_base + [vip, 'protocol', proto])
+ for rs in rservers:
+ self.cli_set(vserver_base + [vip, 'real-server', rs, 'connection-timeout', connection_timeout])
+ self.cli_set(vserver_base + [vip, 'real-server', rs, 'port', rport])
+
+ # commit changes
+ self.cli_commit()
+
+ config = read_file(KEEPALIVED_CONF)
+
+ # Keepalived vrrp
+ self.assertIn(f'# {group}', config)
+ self.assertIn(f'interface {vrrp_interface}', config)
+ self.assertIn(f'virtual_router_id {vrid}', config)
+ self.assertIn(f'priority 100', config) # default value
+ self.assertIn(f'advert_int 1', config) # default value
+ self.assertIn(f'preempt_delay 0', config) # default value
+
+ # Keepalived virtual-server
+ self.assertIn(f'delay_loop {delay}', config)
+ self.assertIn(f'lb_algo lc', config)
+ self.assertIn(f'lb_kind {method.upper()}', config)
+ self.assertIn(f'persistence_timeout {persistence_timeout}', config)
+ self.assertIn(f'protocol {proto.upper()}', config)
+ for rs in rservers:
+ self.assertIn(f'real_server {rs} {rport}', config)
+ self.assertIn(f'{proto.upper()}_CHECK', config)
+ self.assertIn(f'connect_timeout {connection_timeout}', config)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/src/conf_mode/vrrp.py b/src/conf_mode/high-availability.py
index c72efc61f..7d51bb393 100755
--- a/src/conf_mode/vrrp.py
+++ b/src/conf_mode/high-availability.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2021 VyOS maintainers and contributors
+# Copyright (C) 2018-2022 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
@@ -40,33 +40,41 @@ def get_config(config=None):
else:
conf = Config()
- base = ['high-availability', 'vrrp']
+ base = ['high-availability']
+ base_vrrp = ['high-availability', 'vrrp']
if not conf.exists(base):
return None
- vrrp = conf.get_config_dict(base, key_mangling=('-', '_'),
+ ha = conf.get_config_dict(base, key_mangling=('-', '_'),
get_first_key=True, no_tag_node_value_mangle=True)
# We have gathered the dict representation of the CLI, but there are default
# options which we need to update into the dictionary retrived.
- if 'group' in vrrp:
- default_values = defaults(base + ['group'])
- for group in vrrp['group']:
- vrrp['group'][group] = dict_merge(default_values, vrrp['group'][group])
+ if 'vrrp' in ha:
+ if 'group' in ha['vrrp']:
+ default_values_vrrp = defaults(base_vrrp + ['group'])
+ for group in ha['vrrp']['group']:
+ ha['vrrp']['group'][group] = dict_merge(default_values_vrrp, ha['vrrp']['group'][group])
+
+ # Merge per virtual-server default values
+ if 'virtual_server' in ha:
+ default_values = defaults(base + ['virtual-server'])
+ for vs in ha['virtual_server']:
+ ha['virtual_server'][vs] = dict_merge(default_values, ha['virtual_server'][vs])
## Get the sync group used for conntrack-sync
conntrack_path = ['service', 'conntrack-sync', 'failover-mechanism', 'vrrp', 'sync-group']
if conf.exists(conntrack_path):
- vrrp['conntrack_sync_group'] = conf.return_value(conntrack_path)
+ ha['conntrack_sync_group'] = conf.return_value(conntrack_path)
- return vrrp
+ return ha
-def verify(vrrp):
- if not vrrp:
+def verify(ha):
+ if not ha:
return None
used_vrid_if = []
- if 'group' in vrrp:
- for group, group_config in vrrp['group'].items():
+ if 'vrrp' in ha and 'group' in ha['vrrp']:
+ for group, group_config in ha['vrrp']['group'].items():
# Check required fields
if 'vrid' not in group_config:
raise ConfigError(f'VRID is required but not set in VRRP group "{group}"')
@@ -119,24 +127,37 @@ def verify(vrrp):
if is_ipv4(group_config['peer_address']):
raise ConfigError(f'VRRP group "{group}" uses IPv6 but peer-address is IPv4!')
# Check sync groups
- if 'sync_group' in vrrp:
- for sync_group, sync_config in vrrp['sync_group'].items():
+ if 'vrrp' in ha and 'sync_group' in ha['vrrp']:
+ for sync_group, sync_config in ha['vrrp']['sync_group'].items():
if 'member' in sync_config:
for member in sync_config['member']:
- if member not in vrrp['group']:
+ if member not in ha['vrrp']['group']:
raise ConfigError(f'VRRP sync-group "{sync_group}" refers to VRRP group "{member}", '\
'but it does not exist!')
-def generate(vrrp):
- if not vrrp:
+ # Virtual-server
+ if 'virtual_server' in ha:
+ for vs, vs_config in ha['virtual_server'].items():
+ if 'port' not in vs_config:
+ raise ConfigError(f'Port is required but not set for virtual-server "{vs}"')
+ if 'real_server' not in vs_config:
+ raise ConfigError(f'Real-server ip is required but not set for virtual-server "{vs}"')
+ # Real-server
+ for rs, rs_config in vs_config['real_server'].items():
+ if 'port' not in rs_config:
+ raise ConfigError(f'Port is required but not set for virtual-server "{vs}" real-server "{rs}"')
+
+
+def generate(ha):
+ if not ha:
return None
- render(VRRP.location['config'], 'vrrp/keepalived.conf.tmpl', vrrp)
+ render(VRRP.location['config'], 'high-availability/keepalived.conf.tmpl', ha)
return None
-def apply(vrrp):
+def apply(ha):
service_name = 'keepalived.service'
- if not vrrp:
+ if not ha:
call(f'systemctl stop {service_name}')
return None