From c6ac61aa53c72c433a8520b276720a6424e9c3c3 Mon Sep 17 00:00:00 2001 From: Jeltz Date: Sat, 27 Aug 2022 11:15:18 +0200 Subject: [PATCH 1/4] keepalived: add minimal support for keepalived --- playbooks/keepalived.yml | 25 ++++++ roles/keepalived/defaults/main.yml | 3 + roles/keepalived/handlers/main.yml | 6 ++ roles/keepalived/tasks/main.yml | 21 +++++ roles/keepalived/templates/keepalived.conf.j2 | 83 +++++++++++++++++++ 5 files changed, 138 insertions(+) create mode 100755 playbooks/keepalived.yml create mode 100644 roles/keepalived/defaults/main.yml create mode 100644 roles/keepalived/handlers/main.yml create mode 100644 roles/keepalived/tasks/main.yml create mode 100644 roles/keepalived/templates/keepalived.conf.j2 diff --git a/playbooks/keepalived.yml b/playbooks/keepalived.yml new file mode 100755 index 0000000..4267447 --- /dev/null +++ b/playbooks/keepalived.yml @@ -0,0 +1,25 @@ +#!/usr/bin/env ansible-playbook +--- +- hosts: + - isp-1.rtr.infra.auro.re + - isp-2.rtr.infra.auro.re + vars: + # keepalived__notify_master + # keepalived__notify_backup + # keepalived__notify_fault + keepalived__virtual_router_id: 80 + keepalived__interface: ens18 + keepalived__virtual_addresses: + client-0: + - 100.64.0.1/27 + client-1: + - 100.64.0.33/27 + client-2: + - 100.64.0.65/27 + client-3: + - 100.64.0.97/27 + client-4: + - 100.64.0.129/27 + roles: + - keepalived +... diff --git a/roles/keepalived/defaults/main.yml b/roles/keepalived/defaults/main.yml new file mode 100644 index 0000000..c222175 --- /dev/null +++ b/roles/keepalived/defaults/main.yml @@ -0,0 +1,3 @@ +--- +keepalived__virtual_addresses: {} +... diff --git a/roles/keepalived/handlers/main.yml b/roles/keepalived/handlers/main.yml new file mode 100644 index 0000000..df390cb --- /dev/null +++ b/roles/keepalived/handlers/main.yml @@ -0,0 +1,6 @@ +--- +- name: Reload keepalived + systemd: + name: keepalived.service + state: reloaded +... diff --git a/roles/keepalived/tasks/main.yml b/roles/keepalived/tasks/main.yml new file mode 100644 index 0000000..de1a44a --- /dev/null +++ b/roles/keepalived/tasks/main.yml @@ -0,0 +1,21 @@ +--- +- name: Install keepalived + apt: + name: keepalived + +- name: Configure keepalived + template: + src: keepalived.conf.j2 + dest: /etc/keepalived/keepalived.conf + owner: root + group: root + mode: u=rw,g=,o= + notify: + - Reload keepalived + +- name: Enable and start keepalived + systemd: + name: keepalived + enabled: true + state: started +... diff --git a/roles/keepalived/templates/keepalived.conf.j2 b/roles/keepalived/templates/keepalived.conf.j2 new file mode 100644 index 0000000..264c9bd --- /dev/null +++ b/roles/keepalived/templates/keepalived.conf.j2 @@ -0,0 +1,83 @@ +{{ ansible_managed | comment }} + +global_defs { + dynamic_interfaces + script_user root + enable_script_security + vrrp_version 3 +} + +{% +set ipv4_enabled = + keepalived__ipv4_enabled + | default(keepalived__virtual_addresses.values() + | flatten | ansible.utils.ipv4) +%} +{% +set ipv6_enabled = + keepalived__ipv6_enabled + | default(keepalived__virtual_addresses.values() + | flatten | ansible.utils.ipv6) +%} + +vrrp_sync_group group { + group { +{% if ipv4_enabled %} + instance_v4 +{% endif %} +{% if ipv6_enabled %} + instance_v6 +{% endif %} + } +{% if keepalived__notify_master is defined %} + notify_master {{ keepalived__notify_master | enquote('"') }} +{% endif %} +{% if keepalived__notify_backup is defined %} + notify_backup {{ keepalived__notify_backup | enquote('"') }} +{% endif %} +{% if keepalived__notify_fault is defined %} + notify_fault {{ keepalived__notify_fault | enquote('"') }} +{% endif %} +} + +{% if ipv4_enabled %} +vrrp_instance instance_v4 { + virtual_router_id {{ keepalived__virtual_router_id }} + interface {{ keepalived__interface }} + state BACKUP + priority 250 + nopreempt + advert_int 1 + accept + virtual_ipaddress { +{% for dev, addresses in keepalived__virtual_addresses.items() %} +{% for address in addresses %} +{% if address | ansible.utils.ipv4 %} + {{ address }} dev {{ dev }} +{% endif %} +{% endfor %} +{% endfor %} + } +} +{% endif %} + +{% if ipv6_enabled %} +vrrp_instance instance_v6 { + virtual_router_id {{ keepalived__virtual_router_id }} + interface {{ keepalived__interface }} + state BACKUP + priority 250 + nopreempt + advert_int 1 + accept + virtual_ipaddress { +{% for dev, addresses in keepalived__virtual_addresses.items() %} +{% for address in addresses %} +{% if address | ansible.utils.ipv6 %} + {{ address }} dev {{ dev }} +{% endif %} +{% endfor %} +{% endfor %} + } +} +{% endif %} From 9820ae62e8145db215598d567978088cb7850d49 Mon Sep 17 00:00:00 2001 From: Jeltz Date: Sat, 27 Aug 2022 12:55:53 +0200 Subject: [PATCH 2/4] keepalived: better support for notify scripts --- roles/keepalived/defaults/main.yml | 3 ++ roles/keepalived/tasks/main.yml | 13 ++++++-- roles/keepalived/templates/keepalived.conf.j2 | 24 +++++++++----- roles/keepalived/templates/notify.sh.j2 | 33 +++++++++++++++++++ 4 files changed, 61 insertions(+), 12 deletions(-) create mode 100644 roles/keepalived/templates/notify.sh.j2 diff --git a/roles/keepalived/defaults/main.yml b/roles/keepalived/defaults/main.yml index c222175..6bbee8b 100644 --- a/roles/keepalived/defaults/main.yml +++ b/roles/keepalived/defaults/main.yml @@ -1,3 +1,6 @@ --- keepalived__virtual_addresses: {} +keepalived__notify_master: [] +keepalived__notify_backup: [] +keepalived__notify_fault: [] ... diff --git a/roles/keepalived/tasks/main.yml b/roles/keepalived/tasks/main.yml index de1a44a..6330901 100644 --- a/roles/keepalived/tasks/main.yml +++ b/roles/keepalived/tasks/main.yml @@ -5,11 +5,18 @@ - name: Configure keepalived template: - src: keepalived.conf.j2 - dest: /etc/keepalived/keepalived.conf + src: "{{ item.src }}" + dest: "{{ item.dest }}" owner: root group: root - mode: u=rw,g=,o= + mode: "{{ item.mode }}" + loop: + - src: keepalived.conf.j2 + dest: /etc/keepalived/keepalived.conf + mode: u=rw,g=,o= + - src: notify.sh.j2 + dest: /etc/keepalived/notify.sh + mode: u=rwx,g=,o= notify: - Reload keepalived diff --git a/roles/keepalived/templates/keepalived.conf.j2 b/roles/keepalived/templates/keepalived.conf.j2 index 264c9bd..257e44c 100644 --- a/roles/keepalived/templates/keepalived.conf.j2 +++ b/roles/keepalived/templates/keepalived.conf.j2 @@ -20,6 +20,7 @@ set ipv6_enabled = | flatten | ansible.utils.ipv6) %} +{% if ipv4_enabled and ipv6_enabled %} vrrp_sync_group group { group { {% if ipv4_enabled %} @@ -29,16 +30,11 @@ vrrp_sync_group group { instance_v6 {% endif %} } -{% if keepalived__notify_master is defined %} - notify_master {{ keepalived__notify_master | enquote('"') }} -{% endif %} -{% if keepalived__notify_backup is defined %} - notify_backup {{ keepalived__notify_backup | enquote('"') }} -{% endif %} -{% if keepalived__notify_fault is defined %} - notify_fault {{ keepalived__notify_fault | enquote('"') }} -{% endif %} + notify_master "/etc/keepalived/notify.sh master" + notify_backup "/etc/keepalived/notify.sh backup" + notify_fault "/etc/keepalived/notify.sh fault" } +{% endif %} {% if ipv4_enabled %} vrrp_instance instance_v4 { @@ -58,6 +54,11 @@ vrrp_instance instance_v4 { {% endfor %} {% endfor %} } +{% if not (ipv4_enabled and ipv6_enabled) %} + notify_master "/etc/keepalived/notify.sh master" + notify_backup "/etc/keepalived/notify.sh backup" + notify_fault "/etc/keepalived/notify.sh fault" +{% endif %} } {% endif %} @@ -79,5 +80,10 @@ vrrp_instance instance_v6 { {% endfor %} {% endfor %} } +{% if not (ipv4_enabled and ipv6_enabled) %} + notify_master "/etc/keepalived/notify.sh master" + notify_backup "/etc/keepalived/notify.sh backup" + notify_fault "/etc/keepalived/notify.sh fault" +{% endif %} } {% endif %} diff --git a/roles/keepalived/templates/notify.sh.j2 b/roles/keepalived/templates/notify.sh.j2 new file mode 100644 index 0000000..4f58259 --- /dev/null +++ b/roles/keepalived/templates/notify.sh.j2 @@ -0,0 +1,33 @@ +#!/bin/bash + +master=( +{% for notify in keepalived__notify_master %} + {{ notify | quote }} +{% endfor %} +) + +backup=( +{% for notify in keepalived__notify_backup %} + {{ notify | quote }} +{% endfor %} +) + +fault=( +{% for notify in keepalived__notify_fault %} + {{ notify | quote }} +{% endfor %} +) + +case "$1" in + master | backup | fault) + scripts="$1[@]" + ;; + *) + echo "Usage: $0 (master|backup|fault)" >&2 + exit 1 +esac + +for script in "${!scripts}" +do + eval "${script}" +done From 1d409506042b78d39eb86f5d9c70f5f17ea08715 Mon Sep 17 00:00:00 2001 From: Jeltz Date: Sat, 27 Aug 2022 13:17:35 +0200 Subject: [PATCH 3/4] keepalived: add IPv6 support in playbook --- playbooks/keepalived.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/playbooks/keepalived.yml b/playbooks/keepalived.yml index 4267447..e8239a1 100755 --- a/playbooks/keepalived.yml +++ b/playbooks/keepalived.yml @@ -4,22 +4,29 @@ - isp-1.rtr.infra.auro.re - isp-2.rtr.infra.auro.re vars: - # keepalived__notify_master - # keepalived__notify_backup - # keepalived__notify_fault keepalived__virtual_router_id: 80 keepalived__interface: ens18 keepalived__virtual_addresses: client-0: - 100.64.0.1/27 + - 2a09:6841::/56 + - fe80::1/10 client-1: - 100.64.0.33/27 + - 2a09:6841:0:100::/56 + - fe80::1/10 client-2: - 100.64.0.65/27 + - 2a09:6841:0:100::/56 + - fe80::1/10 client-3: - 100.64.0.97/27 + - 2a09:6841:0:200::/56 + - fe80::1/10 client-4: - 100.64.0.129/27 + - 2a09:6841:0:300::/56 + - fe80::1/10 roles: - keepalived ... From e86b17094be9ccd36dfe6ffac27d7e097ab80146 Mon Sep 17 00:00:00 2001 From: Jeltz Date: Sat, 27 Aug 2022 13:47:08 +0200 Subject: [PATCH 4/4] keepalived: print link-local VIP first + silence priority warning --- filter_plugins/net_utils.py | 27 +++++++++++++++++++ roles/keepalived/defaults/main.yml | 1 + roles/keepalived/templates/keepalived.conf.j2 | 9 ++++--- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/filter_plugins/net_utils.py b/filter_plugins/net_utils.py index 082f34d..5eecace 100644 --- a/filter_plugins/net_utils.py +++ b/filter_plugins/net_utils.py @@ -1,3 +1,6 @@ +import ipaddress +from operator import attrgetter + import dns.name @@ -5,9 +8,33 @@ class FilterModule: def filters(self): return { "remove_domain_suffix": remove_domain_suffix, + "ipaddr_sort": ipaddr_sort, } def remove_domain_suffix(name): parent = dns.name.from_text(name).parent() return parent.to_text() + + +def ipaddr_sort(addrs, types, unknown_after=True): + check_types = { + "global": attrgetter("is_global"), + "link-local": attrgetter("is_link_local"), + "loopback": attrgetter("is_loopback"), + "multicast": attrgetter("is_multicast"), + "private": attrgetter("is_private"), + "reserved": attrgetter("is_reserved"), + "site_local": attrgetter("is_site_local"), + "unspecified": attrgetter("is_unspecified"), + } + + def addr_weight(addr): + if isinstance(addr, str): + addr = ipaddress.ip_address(addr.split("/")[0]) + for index, ty in enumerate(types): + if check_types[ty](ipaddress.ip_address(addr)): + return index + return len(types) if unknown_after else -1 + + return sorted(addrs, key=addr_weight) diff --git a/roles/keepalived/defaults/main.yml b/roles/keepalived/defaults/main.yml index 6bbee8b..ee034f3 100644 --- a/roles/keepalived/defaults/main.yml +++ b/roles/keepalived/defaults/main.yml @@ -3,4 +3,5 @@ keepalived__virtual_addresses: {} keepalived__notify_master: [] keepalived__notify_backup: [] keepalived__notify_fault: [] +keepalived__max_auto_priority: -1 ... diff --git a/roles/keepalived/templates/keepalived.conf.j2 b/roles/keepalived/templates/keepalived.conf.j2 index 257e44c..c99ae10 100644 --- a/roles/keepalived/templates/keepalived.conf.j2 +++ b/roles/keepalived/templates/keepalived.conf.j2 @@ -5,6 +5,9 @@ global_defs { script_user root enable_script_security vrrp_version 3 +{% if keepalived__max_auto_priority is defined %} + max_auto_priority {{ keepalived__max_auto_priority | int }} +{% endif %} } {% @@ -38,7 +41,7 @@ vrrp_sync_group group { {% if ipv4_enabled %} vrrp_instance instance_v4 { - virtual_router_id {{ keepalived__virtual_router_id }} + virtual_router_id {{ keepalived__virtual_router_id | int }} interface {{ keepalived__interface }} state BACKUP priority 250 @@ -64,7 +67,7 @@ vrrp_instance instance_v4 { {% if ipv6_enabled %} vrrp_instance instance_v6 { - virtual_router_id {{ keepalived__virtual_router_id }} + virtual_router_id {{ keepalived__virtual_router_id | int }} interface {{ keepalived__interface }} state BACKUP priority 250 @@ -73,7 +76,7 @@ vrrp_instance instance_v6 { accept virtual_ipaddress { {% for dev, addresses in keepalived__virtual_addresses.items() %} -{% for address in addresses %} +{% for address in addresses | ipaddr_sort(["link-local"]) %} {% if address | ansible.utils.ipv6 %} {{ address }} dev {{ dev }} {% endif %}