From 2990e7e9694d29fb71671ef330bfc7ad4f730166 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es>
Date: Thu, 6 Mar 2025 08:25:26 +0100
Subject: [PATCH 01/34] refs #8553 - add ntp_servers variable

---
 roles/dhcp/templates/dhcpd.conf | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/roles/dhcp/templates/dhcpd.conf b/roles/dhcp/templates/dhcpd.conf
index 47f5af2..7059f42 100644
--- a/roles/dhcp/templates/dhcpd.conf
+++ b/roles/dhcp/templates/dhcpd.conf
@@ -14,7 +14,7 @@ max-lease-time 86400;
 
 option domain-name "{{ domain_name.name }}";
 option domain-name-servers {{ domain_name.servers }};
-option ntp-servers {{ ntp_servers }};
+option ntp-servers {{ domain_name.ntp_servers }};
 
 # DHCP daemon uses default time zone UTC
 db-time-format local;

From 7d7d855384c0690f8cbcc26b11aa6689c66d1b09 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es>
Date: Wed, 12 Mar 2025 14:22:50 +0100
Subject: [PATCH 02/34] db: refs #8414 - Add jenkins Url and nrpe threshold

---
 roles/db/files/nrpe/95-mariadb.cfg             | 1 +
 roles/db/templates/mariabackup/apply.config.sh | 4 ++--
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/roles/db/files/nrpe/95-mariadb.cfg b/roles/db/files/nrpe/95-mariadb.cfg
index 79e9e36..5e28bf2 100644
--- a/roles/db/files/nrpe/95-mariadb.cfg
+++ b/roles/db/files/nrpe/95-mariadb.cfg
@@ -2,3 +2,4 @@ command[check_disk_mysqldata]=/usr/lib/nagios/plugins/check_disk -w 10% -c 5% -p
 command[check_disk_mysqlbin]=/usr/lib/nagios/plugins/check_disk -w 10% -c 5% -p /mnt/mysqlbin
 command[check_disk_backup]=/usr/lib/nagios/plugins/check_disk -w 10% -c 5% -p /mnt/local-backup
 command[check_mysql_scheduler]=/etc/nagios/plugins/check_mysql_scheduler
+command[check_total_procs]=/usr/lib/nagios/plugins/check_procs -w 600 -c 700
diff --git a/roles/db/templates/mariabackup/apply.config.sh b/roles/db/templates/mariabackup/apply.config.sh
index a175dd8..c74176b 100755
--- a/roles/db/templates/mariabackup/apply.config.sh
+++ b/roles/db/templates/mariabackup/apply.config.sh
@@ -15,7 +15,7 @@ dbClusterSiblings=()
 # Jenkins authentication string
 jenkinsAuth=jenkins:{{ lookup(passbolt, 'jenkinsAuth', folder_parent_id=passbolt_folder).password }}
 
-{% if db.jenkinsUrl is defined %}
+{% if db.jenkinsJob is defined %}
 # Jenkins job URL
-jenkinsUrl={{ db.jenkinsUrl }}
+jenkinsUrl={{ jenkinsUrl }}{{ db.jenkinsJob }}
 {% endif %}

From eaddc25c38bd7c1eb50c727ff92c16f4d25a48e0 Mon Sep 17 00:00:00 2001
From: Juan Ferrer <juan@verdnatura.es>
Date: Wed, 12 Mar 2025 17:35:47 +0000
Subject: [PATCH 03/34] fix: refs #8762 Fix table cache hitrate

---
 roles/db/files/conf/z90-vn.cnf | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/roles/db/files/conf/z90-vn.cnf b/roles/db/files/conf/z90-vn.cnf
index 1911135..c7db964 100644
--- a/roles/db/files/conf/z90-vn.cnf
+++ b/roles/db/files/conf/z90-vn.cnf
@@ -30,7 +30,7 @@ interactive_timeout                         = 1800
 wait_timeout                                = 1800
 open_files_limit                            = 20000
 low_priority_updates                        = 1
-table_open_cache                            = 40000
+table_open_cache                            = 50000
 table_definition_cache                      = 10000
 table_open_cache_instances                  = 1
 key_buffer_size                             = 256K

From 00239750a21688a8f0d68a05c9db5420235a5ee2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es>
Date: Fri, 14 Mar 2025 11:29:31 +0100
Subject: [PATCH 04/34] vpn: refs #8748 - Initial approche

---
 playbooks/vpn-ipsec.yml                  |  6 ++++
 roles/ipsec/defaults/main.yml            | 10 ++++++
 roles/ipsec/files/vn.conf                | 19 +++++++++++
 roles/ipsec/handlers/main.yml            |  4 +++
 roles/ipsec/tasks/ipsec.yml              | 43 ++++++++++++++++++++++++
 roles/ipsec/tasks/main.yml               |  3 ++
 roles/ipsec/templates/ipsec.conf         | 32 ++++++++++++++++++
 roles/ipsec/templates/ipsec.secrets      |  2 ++
 roles/ipsec/templates/vn-attr.conf       |  8 +++++
 roles/ipsec/templates/vn-eap-radius.conf | 21 ++++++++++++
 10 files changed, 148 insertions(+)
 create mode 100644 playbooks/vpn-ipsec.yml
 create mode 100644 roles/ipsec/defaults/main.yml
 create mode 100644 roles/ipsec/files/vn.conf
 create mode 100644 roles/ipsec/handlers/main.yml
 create mode 100644 roles/ipsec/tasks/ipsec.yml
 create mode 100644 roles/ipsec/tasks/main.yml
 create mode 100644 roles/ipsec/templates/ipsec.conf
 create mode 100644 roles/ipsec/templates/ipsec.secrets
 create mode 100644 roles/ipsec/templates/vn-attr.conf
 create mode 100644 roles/ipsec/templates/vn-eap-radius.conf

diff --git a/playbooks/vpn-ipsec.yml b/playbooks/vpn-ipsec.yml
new file mode 100644
index 0000000..c8f0979
--- /dev/null
+++ b/playbooks/vpn-ipsec.yml
@@ -0,0 +1,6 @@
+- name: Configure DHCP
+  hosts: all
+  tasks:
+  - name: Configure services to install in the server
+    import_role:
+      name: ipsec
\ No newline at end of file
diff --git a/roles/ipsec/defaults/main.yml b/roles/ipsec/defaults/main.yml
new file mode 100644
index 0000000..9113d34
--- /dev/null
+++ b/roles/ipsec/defaults/main.yml
@@ -0,0 +1,10 @@
+strongswan_requeriments:
+  - strongswan
+  - libstrongswan-standard-plugins
+  - strongswan-pki
+  - tcpdump
+  - iperf
+  - conntrack
+certificates:
+  - { content: '{{ cert_ipsec }}', dest: '/etc/ipsec.d/certs/cert.pem', mode: 'u=rw,g=r,o=r' }
+  - { content: '{{ ca }}', dest: '/etc/ipsec.d/cacerts/ca.pem', mode: 'u=rw,g=r,o=r' }
\ No newline at end of file
diff --git a/roles/ipsec/files/vn.conf b/roles/ipsec/files/vn.conf
new file mode 100644
index 0000000..0b26373
--- /dev/null
+++ b/roles/ipsec/files/vn.conf
@@ -0,0 +1,19 @@
+charon {
+	cisco_unity = yes
+
+	filelog {
+		log {
+			path = /var/log/strongswan/charon.log
+			append = yes
+			default = 1
+			flush_line = yes
+			ike_name = yes
+			time_format = %Y-%m-%d %H:%M:%S
+		}
+	}
+	syslog {
+		identifier = charon
+		daemon {
+		}
+	}
+}
diff --git a/roles/ipsec/handlers/main.yml b/roles/ipsec/handlers/main.yml
new file mode 100644
index 0000000..79978af
--- /dev/null
+++ b/roles/ipsec/handlers/main.yml
@@ -0,0 +1,4 @@
+- name: restart-ipsec
+  systemd:
+    name: strongswan-starter.service
+    state: restarted
diff --git a/roles/ipsec/tasks/ipsec.yml b/roles/ipsec/tasks/ipsec.yml
new file mode 100644
index 0000000..0b786ba
--- /dev/null
+++ b/roles/ipsec/tasks/ipsec.yml
@@ -0,0 +1,43 @@
+- name: Update apt cache
+  apt:
+   update_cache: yes
+- name: Install VPN package requirements
+  apt:
+    name: "{{ strongswan_requeriments }}"
+    state: present
+    install_recommends: no
+- name: Insert certificates
+  no_log: true
+  copy:
+    content: "{{ item.content }}"
+    dest: "{{ item.dest }}"
+    owner: root
+    group: root
+    mode: "{{ item.mode }}"
+  loop: "{{ certificates }}"
+- name: Add private key
+  copy:
+    content: "{{ lookup(passbolt, 'ipsec_private_key', folder_parent_id=passbolt_folder).description }}"
+    dest: /etc/ipsec.d/private/key.pem
+    owner: root
+    group: root
+    mode: u=r,g=r,o=
+- name: Configure ipsec.conf and charon
+  template:
+    src: "{{ item.src }}"
+    dest: "{{ item.dest }}"
+    owner: root
+    group: root
+    mode: "{{ item.mode }}"
+  loop:
+    - { src: 'ipsec.conf', dest: '/etc/ipsec.conf', mode: 'u=rw,g=r,o=r' }
+    - { src: 'vn-attr.conf', dest: '/etc/strongswan.d/charon/vn-attr.conf', mode: 'u=rw,g=r,o=r' }
+    - { src: 'vn-eap-radius.conf', dest: '/etc/strongswan.d/charon/vn-eap-radius.conf', mode: 'u=r,g=,o=' }
+    - { src: 'ipsec.secrets', dest: '/etc/ipsec.secrets', mode: 'u=r,g=,o=' }
+- name: Copy Configure file
+  copy:
+    src: vn.conf
+    dest: /etc/strongswan.d/vn.conf
+    owner: root
+    group: root
+    mode: u=rw,g=r,o=r
\ No newline at end of file
diff --git a/roles/ipsec/tasks/main.yml b/roles/ipsec/tasks/main.yml
new file mode 100644
index 0000000..d3dd860
--- /dev/null
+++ b/roles/ipsec/tasks/main.yml
@@ -0,0 +1,3 @@
+- import_tasks: ipsec.yml
+  tags: ipsec
+
diff --git a/roles/ipsec/templates/ipsec.conf b/roles/ipsec/templates/ipsec.conf
new file mode 100644
index 0000000..76d3627
--- /dev/null
+++ b/roles/ipsec/templates/ipsec.conf
@@ -0,0 +1,32 @@
+
+config setup
+  charondebug="ike 1, knl 1, cfg 0"
+  uniqueids=no
+
+conn %default
+  auto=add
+  compress=no
+  type=tunnel
+  keyexchange=ikev2
+  fragmentation=yes
+  forceencaps=yes
+  eap_identity=%identity
+
+  dpdaction=clear
+  dpddelay=300s
+  rekey=no
+
+  left=%any
+  leftid=@{{ leftid }}
+  leftcert=cert.pem
+  leftsendcert=always
+  leftsubnet={{ leftsubnet  }}
+
+  right=%any
+  rightid=%any
+  rightauth=eap-radius
+  rightdns={{ rightdns }}
+  rightsendcert=never
+
+{{ ipsec_groups }}
+
diff --git a/roles/ipsec/templates/ipsec.secrets b/roles/ipsec/templates/ipsec.secrets
new file mode 100644
index 0000000..9956a00
--- /dev/null
+++ b/roles/ipsec/templates/ipsec.secrets
@@ -0,0 +1,2 @@
+{{ leftid }} : RSA "key.pem"
+admin %any% : EAP "{{ lookup(passbolt, 'eap', folder_parent_id=passbolt_folder).password }}"
diff --git a/roles/ipsec/templates/vn-attr.conf b/roles/ipsec/templates/vn-attr.conf
new file mode 100644
index 0000000..94b2b2f
--- /dev/null
+++ b/roles/ipsec/templates/vn-attr.conf
@@ -0,0 +1,8 @@
+attr {
+	load = yes
+	dns = {{ rightdns }}
+	split-include = {{ leftsubnet }}
+	split-exclude = 0.0.0.0/0
+	28674 = {{ leftid }}
+	25 = {{ leftid }}
+}
diff --git a/roles/ipsec/templates/vn-eap-radius.conf b/roles/ipsec/templates/vn-eap-radius.conf
new file mode 100644
index 0000000..de69c64
--- /dev/null
+++ b/roles/ipsec/templates/vn-eap-radius.conf
@@ -0,0 +1,21 @@
+eap-radius {
+	load = yes
+	accounting = yes
+	class_group = yes
+	servers {
+		primary {
+			#address = radius1.verdnatura.es
+			address = {{ address_radiusA }}
+			auth_port = {{ auth_port }}
+			acct_port = {{ acct_port }}
+			secret = {{ lookup(passbolt, 'eap-radius', folder_parent_id=passbolt_folder).password }}
+		}
+		secondary {
+			#address = radius2.verdnatura.es
+			address = {{ address_radiusB }}
+			auth_port = {{ auth_port }}
+			acct_port = {{ acct_port }}
+			secret = {{ lookup(passbolt, 'eap-radius', folder_parent_id=passbolt_folder).password }}
+		}
+	}
+}
\ No newline at end of file

From 9ac8501f148d597b3e6a9d4810c0eadd1ec6969b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es>
Date: Fri, 14 Mar 2025 11:33:01 +0100
Subject: [PATCH 05/34] vpn: refs #8748 - Names fix

---
 playbooks/dhcp.yml          | 2 +-
 roles/ipsec/tasks/ipsec.yml | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/playbooks/dhcp.yml b/playbooks/dhcp.yml
index 9a89db8..ebfc0c5 100644
--- a/playbooks/dhcp.yml
+++ b/playbooks/dhcp.yml
@@ -1,4 +1,4 @@
-- name: Configure DHCP
+- name: Configure IPsec StrongSwan
   hosts: all
   tasks:
   - name: Configure services to install in the server
diff --git a/roles/ipsec/tasks/ipsec.yml b/roles/ipsec/tasks/ipsec.yml
index 0b786ba..ce822dd 100644
--- a/roles/ipsec/tasks/ipsec.yml
+++ b/roles/ipsec/tasks/ipsec.yml
@@ -22,7 +22,7 @@
     owner: root
     group: root
     mode: u=r,g=r,o=
-- name: Configure ipsec.conf and charon
+- name: Configure ipsec and charon
   template:
     src: "{{ item.src }}"
     dest: "{{ item.dest }}"

From 8c168d4fa872861f0a9d7a872c2ebe459b38c4b6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es>
Date: Fri, 14 Mar 2025 11:34:21 +0100
Subject: [PATCH 06/34] vpn: refs #8748 - Names fix

---
 playbooks/dhcp.yml      | 2 +-
 playbooks/vpn-ipsec.yml | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/playbooks/dhcp.yml b/playbooks/dhcp.yml
index ebfc0c5..9a89db8 100644
--- a/playbooks/dhcp.yml
+++ b/playbooks/dhcp.yml
@@ -1,4 +1,4 @@
-- name: Configure IPsec StrongSwan
+- name: Configure DHCP
   hosts: all
   tasks:
   - name: Configure services to install in the server
diff --git a/playbooks/vpn-ipsec.yml b/playbooks/vpn-ipsec.yml
index c8f0979..aa9b29a 100644
--- a/playbooks/vpn-ipsec.yml
+++ b/playbooks/vpn-ipsec.yml
@@ -1,4 +1,4 @@
-- name: Configure DHCP
+- name: Configure IPsec StrongSwan
   hosts: all
   tasks:
   - name: Configure services to install in the server

From 0e393b49c8f76e25e2c055e247744bf8a73547ea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es>
Date: Fri, 14 Mar 2025 11:41:13 +0100
Subject: [PATCH 07/34] vpn: refs #8748 - Variables array

---
 roles/ipsec/defaults/main.yml | 7 ++++++-
 roles/ipsec/tasks/ipsec.yml   | 6 +-----
 2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/roles/ipsec/defaults/main.yml b/roles/ipsec/defaults/main.yml
index 9113d34..0553fc3 100644
--- a/roles/ipsec/defaults/main.yml
+++ b/roles/ipsec/defaults/main.yml
@@ -7,4 +7,9 @@ strongswan_requeriments:
   - conntrack
 certificates:
   - { content: '{{ cert_ipsec }}', dest: '/etc/ipsec.d/certs/cert.pem', mode: 'u=rw,g=r,o=r' }
-  - { content: '{{ ca }}', dest: '/etc/ipsec.d/cacerts/ca.pem', mode: 'u=rw,g=r,o=r' }
\ No newline at end of file
+  - { content: '{{ ca }}', dest: '/etc/ipsec.d/cacerts/ca.pem', mode: 'u=rw,g=r,o=r' }
+config_ipsec_files:
+  - { src: 'ipsec.conf', dest: '/etc/ipsec.conf', mode: 'u=rw,g=r,o=r' }
+  - { src: 'vn-attr.conf', dest: '/etc/strongswan.d/charon/vn-attr.conf', mode: 'u=rw,g=r,o=r' }
+  - { src: 'vn-eap-radius.conf', dest: '/etc/strongswan.d/charon/vn-eap-radius.conf', mode: 'u=r,g=,o=' }
+  - { src: 'ipsec.secrets', dest: '/etc/ipsec.secrets', mode: 'u=r,g=,o=' }
diff --git a/roles/ipsec/tasks/ipsec.yml b/roles/ipsec/tasks/ipsec.yml
index ce822dd..36ad57e 100644
--- a/roles/ipsec/tasks/ipsec.yml
+++ b/roles/ipsec/tasks/ipsec.yml
@@ -29,11 +29,7 @@
     owner: root
     group: root
     mode: "{{ item.mode }}"
-  loop:
-    - { src: 'ipsec.conf', dest: '/etc/ipsec.conf', mode: 'u=rw,g=r,o=r' }
-    - { src: 'vn-attr.conf', dest: '/etc/strongswan.d/charon/vn-attr.conf', mode: 'u=rw,g=r,o=r' }
-    - { src: 'vn-eap-radius.conf', dest: '/etc/strongswan.d/charon/vn-eap-radius.conf', mode: 'u=r,g=,o=' }
-    - { src: 'ipsec.secrets', dest: '/etc/ipsec.secrets', mode: 'u=r,g=,o=' }
+  loop: "{{ config_ipsec_files }}"
 - name: Copy Configure file
   copy:
     src: vn.conf

From c1074a90e51fbeb4804168c6238a8fb7ec37f95c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es>
Date: Fri, 14 Mar 2025 12:58:15 +0100
Subject: [PATCH 08/34] vpn: refs #8748 - Iptables approche - what to do

---
 roles/ipsec/defaults/main.yml |  1 +
 roles/ipsec/tasks/ipsec.yml   | 26 +++++++++++++++++++++++++-
 2 files changed, 26 insertions(+), 1 deletion(-)

diff --git a/roles/ipsec/defaults/main.yml b/roles/ipsec/defaults/main.yml
index 0553fc3..fedeaef 100644
--- a/roles/ipsec/defaults/main.yml
+++ b/roles/ipsec/defaults/main.yml
@@ -5,6 +5,7 @@ strongswan_requeriments:
   - tcpdump
   - iperf
   - conntrack
+  - iptables-persistent
 certificates:
   - { content: '{{ cert_ipsec }}', dest: '/etc/ipsec.d/certs/cert.pem', mode: 'u=rw,g=r,o=r' }
   - { content: '{{ ca }}', dest: '/etc/ipsec.d/cacerts/ca.pem', mode: 'u=rw,g=r,o=r' }
diff --git a/roles/ipsec/tasks/ipsec.yml b/roles/ipsec/tasks/ipsec.yml
index 36ad57e..a1ed9cd 100644
--- a/roles/ipsec/tasks/ipsec.yml
+++ b/roles/ipsec/tasks/ipsec.yml
@@ -36,4 +36,28 @@
     dest: /etc/strongswan.d/vn.conf
     owner: root
     group: root
-    mode: u=rw,g=r,o=r
\ No newline at end of file
+    mode: u=rw,g=r,o=r
+- name: IP forward as a router
+  sysctl:
+    name: net.ipv4.ip_forward
+    value: "1"
+    state: present
+    sysctl_set: yes
+    reload: yes
+- name: Add iptables rules in rules.v4 file
+  blockinfile:
+    path: /etc/iptables/rules.v4
+    marker: "# {mark} ANSIBLE-MANAGED MANGLE CHAIN MANGED"
+    block: |
+      *mangle
+      :PREROUTING ACCEPT [0:0]
+      :INPUT ACCEPT [0:0]
+      :FORWARD ACCEPT [0:0]
+      :OUTPUT ACCEPT [0:0]
+      :POSTROUTING ACCEPT [0:0]
+      -A PREROUTING -p tcp -m policy --dir in --pol ipsec -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360
+      -A POSTROUTING -p tcp -m policy --dir out --pol ipsec -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360    
+  register: iptables
+- name: Reload iptables rules
+  command: netfilter-persistent reload
+  when: iptables.changed
\ No newline at end of file

From aba7121907ea0307f829d8d9ee10de75c4fc1f86 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es>
Date: Fri, 14 Mar 2025 14:33:53 +0100
Subject: [PATCH 09/34] vpn: refs #8748 - Final touch

---
 roles/ipsec/defaults/main.yml | 13 +++++++++
 roles/ipsec/files/charon      | 11 ++++++++
 roles/ipsec/tasks/ipsec.yml   | 50 +++++++++++++++++++++++++----------
 3 files changed, 60 insertions(+), 14 deletions(-)
 create mode 100644 roles/ipsec/files/charon

diff --git a/roles/ipsec/defaults/main.yml b/roles/ipsec/defaults/main.yml
index fedeaef..c8b1cd0 100644
--- a/roles/ipsec/defaults/main.yml
+++ b/roles/ipsec/defaults/main.yml
@@ -14,3 +14,16 @@ config_ipsec_files:
   - { src: 'vn-attr.conf', dest: '/etc/strongswan.d/charon/vn-attr.conf', mode: 'u=rw,g=r,o=r' }
   - { src: 'vn-eap-radius.conf', dest: '/etc/strongswan.d/charon/vn-eap-radius.conf', mode: 'u=r,g=,o=' }
   - { src: 'ipsec.secrets', dest: '/etc/ipsec.secrets', mode: 'u=r,g=,o=' }
+mangle_block: |
+  *mangle
+  :PREROUTING ACCEPT [0:0]
+  :INPUT ACCEPT [0:0]
+  :FORWARD ACCEPT [0:0]
+  :OUTPUT ACCEPT [0:0]
+  :POSTROUTING ACCEPT [0:0]
+  -A PREROUTING -p tcp -m policy --dir in --pol ipsec -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360
+  -A POSTROUTING -p tcp -m policy --dir out --pol ipsec -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360
+  COMMIT
+config_and_logrotate:
+  - { src: vn.conf, dest: '/etc/strongswan.d/vn.conf' }
+  - { src: charon, dest: '/etc/logrotate.d/charon' }
diff --git a/roles/ipsec/files/charon b/roles/ipsec/files/charon
new file mode 100644
index 0000000..9a05de0
--- /dev/null
+++ b/roles/ipsec/files/charon
@@ -0,0 +1,11 @@
+/var/log/strongswan/charon.log
+{
+	copytruncate
+	create 644 root root
+        rotate 10
+        weekly
+        missingok
+        notifempty
+        compress
+	delaycompress
+}
diff --git a/roles/ipsec/tasks/ipsec.yml b/roles/ipsec/tasks/ipsec.yml
index a1ed9cd..37f63ce 100644
--- a/roles/ipsec/tasks/ipsec.yml
+++ b/roles/ipsec/tasks/ipsec.yml
@@ -6,6 +6,13 @@
     name: "{{ strongswan_requeriments }}"
     state: present
     install_recommends: no
+- name: Create directory /var/log/strongswan
+  file:
+    path: /var/log/strongswan
+    state: directory
+    owner: root
+    group: root
+    mode: '0755'
 - name: Insert certificates
   no_log: true
   copy:
@@ -30,13 +37,16 @@
     group: root
     mode: "{{ item.mode }}"
   loop: "{{ config_ipsec_files }}"
-- name: Copy Configure file
+  notify: restart-ipsec
+- name: Copy Configure file and logrotate Charon
   copy:
-    src: vn.conf
-    dest: /etc/strongswan.d/vn.conf
+    src: "{{ item.src }}"
+    dest: "{{ item.dest }}"
     owner: root
     group: root
     mode: u=rw,g=r,o=r
+  loop: "{{ config_and_logrotate }}"
+  notify: restart-ipsec
 - name: IP forward as a router
   sysctl:
     name: net.ipv4.ip_forward
@@ -47,17 +57,29 @@
 - name: Add iptables rules in rules.v4 file
   blockinfile:
     path: /etc/iptables/rules.v4
-    marker: "# {mark} ANSIBLE-MANAGED MANGLE CHAIN MANGED"
-    block: |
-      *mangle
-      :PREROUTING ACCEPT [0:0]
-      :INPUT ACCEPT [0:0]
-      :FORWARD ACCEPT [0:0]
-      :OUTPUT ACCEPT [0:0]
-      :POSTROUTING ACCEPT [0:0]
-      -A PREROUTING -p tcp -m policy --dir in --pol ipsec -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360
-      -A POSTROUTING -p tcp -m policy --dir out --pol ipsec -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360    
+    marker: "# {mark} ANSIBLE-MANAGED MANGLE CHAIN"
+    block: "{{ mangle_block }}"
   register: iptables
 - name: Reload iptables rules
   command: netfilter-persistent reload
-  when: iptables.changed
\ No newline at end of file
+  when: iptables.changed
+- name: Get default IPv4 interface
+  command: ip -o -4 route show default
+  register: default_route
+- name: Extract interface default name
+  set_fact:
+    active_interface: "{{ default_route.stdout.split()[-1] }}"
+- name: Routing table for VPN
+  lineinfile:
+    path: /etc/iproute2/rt_tables
+    line: "10 vpn"
+    state: present
+    regexp: "vpn"
+- name: Static routing rules to send VPN traffic directly to the firewall
+  lineinfile:
+    path: /etc/network/interfaces
+    insertafter: "dhcp"
+    line: "{{ item }}"
+    state: present
+  loop: "{{ static_routes }}"
+  
\ No newline at end of file

From 7f372d20f4a86c3590c7c2600a902996fb40fca9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es>
Date: Mon, 17 Mar 2025 15:57:11 +0100
Subject: [PATCH 10/34] vpn: refs #8140 - Refactor main yml and add pmm
 template

---
 roles/db/defaults/main.yaml       |  5 ++++
 roles/db/handlers/main.yml        |  4 +++
 roles/db/tasks/mariadb.yml        | 49 ++++++-------------------------
 roles/db/templates/pmm-agent.yaml | 34 +++++++++++++++++++++
 4 files changed, 52 insertions(+), 40 deletions(-)
 create mode 100644 roles/db/templates/pmm-agent.yaml

diff --git a/roles/db/defaults/main.yaml b/roles/db/defaults/main.yaml
index b1818b7..7dd354f 100644
--- a/roles/db/defaults/main.yaml
+++ b/roles/db/defaults/main.yaml
@@ -33,6 +33,11 @@ required_mariabackup_files_and_scripts:
   - { src: scripts/events-demote.sh, dest: /root/scripts/events-demote.sh, mode: u=rwx,g=rx,o=rx }
   - { src: scripts/README.md, dest: /root/scripts/README.md, mode: u=rw,g=r,o=r }
   - { src: scripts/scheduler-log.sh, dest: /root/scripts/scheduler-log.sh, mode: u=rwx,g=rx,o=rx }
+configuration_files:
+  - { src: "conf/z99-local.cnf", dest: "/etc/mysql/mariadb.conf.d/z99-local.cnf", owner: "root", group: "root", mode: "u=rw,g=r,o=r" }
+  - { src: "mariabackup/my.cnf", dest: "/root/mariabackup/my.cnf", owner: "root", group: "root", mode: "u=rw,g=,o=" }
+  - { src: "pmm-agent.yaml", dest: "/usr/local/percona/pmm2/config/pmm-agent.yaml", owner: "pmm-agent", group: "pmm-agent", mode: "u=rw,g=rw,o=" }
+  - { src: "check-memory.cron", dest: "/etc/cron.d/vn-check-memory", owner: "root", group: "root", mode: "u=rw,g=r,o=r" }    
 downloads:
   - url: https://r.mariadb.com/downloads/mariadb_repo_setup
     dest: /tmp/mariadb_repo_setup
diff --git a/roles/db/handlers/main.yml b/roles/db/handlers/main.yml
index 320b475..11143ee 100644
--- a/roles/db/handlers/main.yml
+++ b/roles/db/handlers/main.yml
@@ -9,3 +9,7 @@
   service:
     name: nagios-nrpe-server
     state: restarted
+- name: restart-percona
+  service:
+    name: pmm-agent.service
+    state: restarted
diff --git a/roles/db/tasks/mariadb.yml b/roles/db/tasks/mariadb.yml
index a3980ce..806125a 100644
--- a/roles/db/tasks/mariadb.yml
+++ b/roles/db/tasks/mariadb.yml
@@ -3,35 +3,29 @@
     name: "{{ mariadb_requeriments }}"
     state: present
     install_recommends: no
-
 - name: Download required setup files
   get_url:
     url: "{{ item.url }}"
     dest: "{{ item.dest }}"
     mode: "{{ item.mode }}"
   loop: "{{ downloads }}"
-
 - name: Run MariaDB repository setup script
   command:
     cmd: "/bin/bash /tmp/mariadb_repo_setup --mariadb-server-version={{ db.version | default('10.11.10') }}"
     creates: "/etc/apt/sources.list.d/mariadb.list"
-
 - name: Install Percona repository package
   apt:
     deb: "/tmp/percona-release_latest.generic_all.deb"
     state: present
     install_recommends: no
-
 - name: Update apt cache
   apt:
    update_cache: yes
-
 - name: Install MariaDB packages
   apt:
     name: "{{ mariadb_base_packages }}"
     state: present
     install_recommends: no
-
 - name: Add tmpfs in /etc/fstab
   blockinfile:
     path: /etc/fstab
@@ -39,15 +33,6 @@
     block: |
       tmpfs /mnt/mysqltmp         tmpfs rw,size={{ mysqltmpsize }}         0 0
   register: fstab
-
-- name: Configure MariaDB memory check CRON
-  template:
-    src: check-memory.cron
-    dest: /etc/cron.d/vn-check-memory
-    owner: root
-    group: root
-    mode: u=rw,g=r,o=r
-
 - name: Configure MariaDB scheduler log CRON
   copy:
     src: scheduler-log.cron
@@ -55,7 +40,6 @@
     owner: root
     group: root
     mode: u=rw,g=r,o=r
-
 - name: Insert MySQL certificates
   no_log: true
   copy:
@@ -66,7 +50,6 @@
     mode: "{{ item.mode }}"
   loop: "{{ certificates }}"
   notify: restart-mariadb
-
 - name: Ensure required directories exist
   file:
     path: "{{ item.path }}"
@@ -75,7 +58,6 @@
     group: "{{ item.group }}"
     mode: "{{ item.mode }}"
   loop: "{{ required_directories }}"
-
 - name: Copy required MariaBackup files and scripts
   copy:
     src: "{{ item.src }}"
@@ -84,7 +66,15 @@
     group: root
     mode: "{{ item.mode }}"
   loop: "{{ required_mariabackup_files_and_scripts }}"
-
+- name: Deploy configuration files
+  template:
+    src: "{{ item.src }}"
+    dest: "{{ item.dest }}"
+    owner: "{{ item.owner }}"
+    group: "{{ item.group }}"
+    mode: "{{ item.mode }}"
+  loop: "{{ configuration_files }}" 
+  notify: restart-percona
 - name: Set MariaDB common configuration
   copy:
     src: conf/z90-vn.cnf
@@ -93,23 +83,6 @@
     group: root
     mode: u=rw,g=r,o=r
   notify: restart-mariadb
-
-- name: Set MariaDB local configuration template
-  template:
-    src: conf/z99-local.cnf
-    dest: /etc/mysql/mariadb.conf.d/
-    owner: root
-    group: root
-    mode: u=rw,g=r,o=r
-
-- name: Set MariaBackup connection configuration
-  template:
-    src: mariabackup/my.cnf
-    dest: /root/mariabackup/
-    owner: root
-    group: root
-    mode: u=rw,g=,o=
-
 - name: Override MariaDB systemd service configuration
   copy:
     src: mariadb_override.conf
@@ -118,7 +91,6 @@
     group: root
     mode: u=rw,g=r,o=r
   notify: reload-systemd
-
 - name: Set MariaDB NRPE configuration
   copy:
     src: nrpe/95-mariadb.cfg
@@ -127,12 +99,10 @@
     group: root
     mode: u=rw,g=r,o=r
   notify: restart-nrpe
-
 - name: Check if /var/lib/mysql/ exists
   stat:
     path: /var/lib/mysql/
   register: mysql_dir
-
 - when: mysql_dir.stat.exists
   block:
 
@@ -149,7 +119,6 @@
     file:
       path: /var/lib/mysql/
       state: absent
-
 - name: Mount all filesystems from /etc/fstab
   command: mount -a
   when: fstab.changed
diff --git a/roles/db/templates/pmm-agent.yaml b/roles/db/templates/pmm-agent.yaml
new file mode 100644
index 0000000..04e2b6a
--- /dev/null
+++ b/roles/db/templates/pmm-agent.yaml
@@ -0,0 +1,34 @@
+# Updated by `pmm-agent setup`.
+---
+id: /agent_id/{{ pmm.id }}
+listen-address: 127.0.0.1
+listen-port: 7777
+server:
+    address: {{ pmm.address }}
+    username: {{ pmm.username }}
+    password: {{ pmm.password }}
+    insecure-tls: {{ pmm.insecure }}
+paths:
+    paths_base: /usr/local/percona/pmm2
+    exporters_base: /usr/local/percona/pmm2/exporters
+    node_exporter: /usr/local/percona/pmm2/exporters/node_exporter
+    mysqld_exporter: /usr/local/percona/pmm2/exporters/mysqld_exporter
+    mongodb_exporter: /usr/local/percona/pmm2/exporters/mongodb_exporter
+    postgres_exporter: /usr/local/percona/pmm2/exporters/postgres_exporter
+    proxysql_exporter: /usr/local/percona/pmm2/exporters/proxysql_exporter
+    rds_exporter: /usr/local/percona/pmm2/exporters/rds_exporter
+    azure_exporter: /usr/local/percona/pmm2/exporters/azure_exporter
+    vmagent: /usr/local/percona/pmm2/exporters/vmagent
+    tempdir: {{ pmm.tempdir }}
+    pt_summary: /usr/local/percona/pmm2/tools/pt-summary
+    pt_pg_summary: /usr/local/percona/pmm2/tools/pt-pg-summary
+    pt_mysql_summary: /usr/local/percona/pmm2/tools/pt-mysql-summary
+    pt_mongodb_summary: /usr/local/percona/pmm2/tools/pt-mongodb-summary
+ports:
+    min: 42000
+    max: 51999
+log-level: warn
+debug: false
+trace: false
+loglinescount: 1024
+window-connected-time: 1h0m0s

From aef6cb5bc0b7c90a3e2faa44e26d18abd5431fd4 Mon Sep 17 00:00:00 2001
From: Juan Ferrer Toribio <juan@verdnatura.es>
Date: Mon, 17 Mar 2025 17:49:06 +0100
Subject: [PATCH 11/34] refs #8776 PVE and Ceph monitoring

---
 roles/ceph/files/nrpe.cfg                     |    2 +-
 roles/ceph/files/nrpe/check_ceph_mon          |  163 +++
 roles/ceph/files/nrpe/check_ceph_osd          |  154 ++
 roles/ceph/files/nrpe/check_chrony            |  128 ++
 .../files/nrpe/check_smartdisk.sh             |    0
 roles/ceph/files/sudoers                      |    1 +
 roles/ceph/tasks/main.yml                     |   10 +-
 roles/ceph/tasks/nrpe.yml                     |   24 +
 .../pve/files/{nrpe.cfg => nrpe.d/95-pve.cfg} |    0
 .../pve/files/{nrpe => plugins}/check_chrony  |    0
 roles/pve/files/plugins/check_ilo2_health.pl  | 1234 +++++++++++++++++
 roles/pve/files/plugins/check_smartdisk.sh    |   22 +
 .../pve/files/{nrpe => plugins}/check_zfs.pl  |    0
 roles/pve/tasks/nrpe.yml                      |   12 +-
 roles/pve/tasks/vhost.yml                     |   10 +-
 15 files changed, 1743 insertions(+), 17 deletions(-)
 create mode 100755 roles/ceph/files/nrpe/check_ceph_mon
 create mode 100755 roles/ceph/files/nrpe/check_ceph_osd
 create mode 100755 roles/ceph/files/nrpe/check_chrony
 rename roles/{pve => ceph}/files/nrpe/check_smartdisk.sh (100%)
 create mode 100644 roles/ceph/files/sudoers
 create mode 100644 roles/ceph/tasks/nrpe.yml
 rename roles/pve/files/{nrpe.cfg => nrpe.d/95-pve.cfg} (100%)
 rename roles/pve/files/{nrpe => plugins}/check_chrony (100%)
 create mode 100755 roles/pve/files/plugins/check_ilo2_health.pl
 create mode 100755 roles/pve/files/plugins/check_smartdisk.sh
 rename roles/pve/files/{nrpe => plugins}/check_zfs.pl (100%)

diff --git a/roles/ceph/files/nrpe.cfg b/roles/ceph/files/nrpe.cfg
index 76d252f..afc0035 100644
--- a/roles/ceph/files/nrpe.cfg
+++ b/roles/ceph/files/nrpe.cfg
@@ -1,4 +1,4 @@
-command[check_total_procs]=/usr/lib/nagios/plugins/check_procs -w 700 -c 1000
+command[check_total_procs]=/usr/lib/nagios/plugins/check_procs -w 1300 -c 1500
 command[check_chrony]=/usr/lib/nagios/plugins/check_chrony 1 2
 command[check_smartdisk]=/etc/nagios/plugins/check_smartdisk.sh /dev/sda /dev/sdb
 command[check_raid]=/usr/lib/nagios/plugins/check_raid
diff --git a/roles/ceph/files/nrpe/check_ceph_mon b/roles/ceph/files/nrpe/check_ceph_mon
new file mode 100755
index 0000000..f8decea
--- /dev/null
+++ b/roles/ceph/files/nrpe/check_ceph_mon
@@ -0,0 +1,163 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+#  Copyright (c) 2013 Catalyst IT http://www.catalyst.net.nz
+#  Copyright (c) 2015 SWITCH http://www.switch.ch
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+
+from __future__ import print_function
+import argparse
+import socket
+import os
+import re
+import subprocess
+import sys
+import json
+
+__version__ = '1.5.0'
+
+# default ceph values
+CEPH_EXEC = '/usr/bin/ceph'
+CEPH_COMMAND = 'quorum_status'
+
+# nagios exit code
+STATUS_OK = 0
+STATUS_WARNING = 1
+STATUS_ERROR = 2
+STATUS_UNKNOWN = 3
+
+##
+# ceph quorum_status output example
+##
+ceph_quorum_status_output_example = '''{
+   "quorum_leader_name" : "s0001",
+   "monmap" : {
+      "mons" : [
+         {
+            "name" : "s0001",
+            "addr" : "[2001:620:5ca1:8000::1001]:6789/0",
+            "rank" : 0
+         },
+         {
+            "name" : "s0003",
+            "addr" : "[2001:620:5ca1:8000::1003]:6789/0",
+            "rank" : 1
+         }
+      ],
+      "created" : "2014-12-15 08:28:35.153650",
+      "epoch" : 2,
+      "modified" : "2014-12-15 08:28:40.371878",
+      "fsid" : "22348d2b-b69d-46cc-9a79-ca93cd6bae84"
+   },
+   "quorum_names" : [
+      "s0001",
+      "s0003"
+   ],
+   "quorum" : [
+      0,
+      1
+   ],
+   "election_epoch" : 24
+}'''
+
+def main():
+
+  # parse args
+  parser = argparse.ArgumentParser(description="'ceph quorum_status' nagios plugin.")
+  parser.add_argument('-e','--exe', help='ceph executable [%s]' % CEPH_EXEC)
+  parser.add_argument('-c','--conf', help='alternative ceph conf file')
+  parser.add_argument('-m','--monaddress', help='ceph monitor to use for queries (address[:port])')
+  parser.add_argument('-i','--id', help='ceph client id')
+  parser.add_argument('-k','--keyring', help='ceph client keyring file')
+  parser.add_argument('-V','--version', help='show version and exit', action='store_true')
+  parser.add_argument('-I','--monid', help='mon ID to be checked for availability')
+  args = parser.parse_args()
+
+  if args.version:
+    print('version %s' % __version__)
+    return STATUS_OK
+
+  # validate args
+  ceph_exec = args.exe if args.exe else CEPH_EXEC
+  if not os.path.exists(ceph_exec):
+    print("MON ERROR: ceph executable '%s' doesn't exist" % ceph_exec)
+    return STATUS_UNKNOWN
+
+  if args.conf and not os.path.exists(args.conf):
+    print("MON ERROR: ceph conf file '%s' doesn't exist" % args.conf)
+    return STATUS_UNKNOWN
+
+  if args.keyring and not os.path.exists(args.keyring):
+    print("MON ERROR: keyring file '%s' doesn't exist" % args.keyring)
+    return STATUS_UNKNOWN
+
+  if not args.monid:
+    print("MON ERROR: no MON ID given, use -I/--monid parameter")
+    return STATUS_UNKNOWN
+
+  # build command
+  ceph_cmd = [ceph_exec]
+  if args.monaddress:
+    ceph_cmd.append('-m')
+    ceph_cmd.append(args.monaddress)
+  if args.conf:
+    ceph_cmd.append('-c')
+    ceph_cmd.append(args.conf)
+  if args.id:
+    ceph_cmd.append('--id')
+    ceph_cmd.append(args.id)
+  if args.keyring:
+    ceph_cmd.append('--keyring')
+    ceph_cmd.append(args.keyring)
+  ceph_cmd.append(CEPH_COMMAND)
+
+  # exec command
+  p = subprocess.Popen(ceph_cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
+  output, err = p.communicate()
+
+  if p.returncode != 0 or not output:
+    print("MON ERROR: %s" % err)
+    return STATUS_ERROR
+
+  # load json output and parse
+  quorum_status = False
+  try:
+    quorum_status = json.loads(output)
+  except Exception as e:
+    print("MON ERROR: could not parse '%s' output: %s: %s" % (CEPH_COMMAND,output,e))
+    return STATUS_UNKNOWN
+
+  #print "XXX: quorum_status['quorum_names']:", quorum_status['quorum_names']
+
+  # do our checks
+  is_monitor = False
+  for mon in quorum_status['monmap']['mons']:
+    if mon['name'] == args.monid:
+      is_monitor = True
+  if not is_monitor:
+    print("MON WARN: mon '%s' is not in monmap: %s" % (args.monid,quorum_status['monmap']['mons']))
+    return STATUS_WARNING
+
+  in_quorum = args.monid in quorum_status['quorum_names']
+  if in_quorum:
+    print("MON OK")
+    return STATUS_OK
+  else:
+    print("MON WARN: no MON '%s' found in quorum" % args.monid)
+    return STATUS_WARNING
+
+# main
+if __name__ == "__main__":
+  sys.exit(main())
diff --git a/roles/ceph/files/nrpe/check_ceph_osd b/roles/ceph/files/nrpe/check_ceph_osd
new file mode 100755
index 0000000..2ee9de6
--- /dev/null
+++ b/roles/ceph/files/nrpe/check_ceph_osd
@@ -0,0 +1,154 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+#  Copyright (c) 2013 Catalyst IT http://www.catalyst.net.nz
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+# 1.5.2 (2019-06-16) Martin Seener: fixed regex to work with Ceph Nautilus (14.2.x)
+
+from __future__ import print_function
+import argparse
+import os
+import re
+import subprocess
+import sys
+import socket
+
+__version__ = '1.5.2'
+
+# default ceph values
+CEPH_COMMAND = '/usr/bin/ceph'
+
+# nagios exit code
+STATUS_OK = 0
+STATUS_WARNING = 1
+STATUS_ERROR = 2
+STATUS_UNKNOWN = 3
+
+def main():
+
+  # parse args
+  parser = argparse.ArgumentParser(description="'ceph osd' nagios plugin.")
+  parser.add_argument('-e','--exe', help='ceph executable [%s]' % CEPH_COMMAND)
+  parser.add_argument('-c','--conf', help='alternative ceph conf file')
+  parser.add_argument('-m','--monaddress', help='ceph monitor address[:port]')
+  parser.add_argument('-i','--id', help='ceph client id')
+  parser.add_argument('-k','--keyring', help='ceph client keyring file')
+  parser.add_argument('-V','--version', help='show version and exit', action='store_true')
+  parser.add_argument('-H','--host', help='osd host', required=True)
+  parser.add_argument('-I','--osdid', help='osd id', required=False)
+  parser.add_argument('-C','--crit', help='Number of failed OSDs to trigger critical (default=2)',type=int,default=2, required=False)
+  parser.add_argument('-o','--out', help='check osds that are set OUT', default=False, action='store_true', required=False)
+  args = parser.parse_args()
+
+  # validate args
+  ceph_exec = args.exe if args.exe else CEPH_COMMAND
+  if not os.path.exists(ceph_exec):
+    print("OSD ERROR: ceph executable '%s' doesn't exist" % ceph_exec)
+    return STATUS_UNKNOWN
+
+  if args.version:
+    print('version %s' % __version__)
+    return STATUS_OK
+
+  if args.conf and not os.path.exists(args.conf):
+    print("OSD ERROR: ceph conf file '%s' doesn't exist" % args.conf)
+    return STATUS_UNKNOWN
+
+  if args.keyring and not os.path.exists(args.keyring):
+    print("OSD ERROR: keyring file '%s' doesn't exist" % args.keyring)
+    return STATUS_UNKNOWN
+
+  if not args.osdid:
+    args.osdid = '[^ ]*'
+
+  if not args.host:
+    print("OSD ERROR: no OSD hostname given")
+    return STATUS_UNKNOWN
+
+  try:
+    addrinfo = socket.getaddrinfo(args.host, None, 0, socket.SOCK_STREAM)
+    args.host = addrinfo[0][-1][0]
+    if addrinfo[0][0] == socket.AF_INET6:
+      args.host = "[%s]" % args.host
+  except:
+    print('OSD ERROR: could not resolve %s' % args.host)
+    return STATUS_UNKNOWN
+
+
+  # build command
+  ceph_cmd = [ceph_exec]
+  if args.monaddress:
+      ceph_cmd.append('-m')
+      ceph_cmd.append(args.monaddress)
+  if args.conf:
+      ceph_cmd.append('-c')
+      ceph_cmd.append(args.conf)
+  if args.id:
+      ceph_cmd.append('--id')
+      ceph_cmd.append(args.id)
+  if args.keyring:
+      ceph_cmd.append('--keyring')
+      ceph_cmd.append(args.keyring)
+  ceph_cmd.append('osd')
+  ceph_cmd.append('dump')
+
+  # exec command
+  p = subprocess.Popen(ceph_cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
+  output, err = p.communicate()
+  output = output.decode('utf8')
+
+  if err or not output:
+    print("OSD ERROR: %s" % err)
+    return STATUS_ERROR
+
+  # escape IPv4 host address
+  osd_host = args.host.replace('.', '\.')
+  # escape IPv6 host address
+  osd_host = osd_host.replace('[', '\[')
+  osd_host = osd_host.replace(']', '\]')
+  up = re.findall(r"^(osd\.%s) up.*%s:" % (args.osdid, osd_host), output, re.MULTILINE)
+  if args.out:
+    down = re.findall(r"^(osd\.%s) down.*%s:" % (args.osdid, osd_host), output, re.MULTILINE)
+    down_in = re.findall(r"^(osd\.%s) down[ ]+in.*%s:" % (args.osdid, osd_host), output, re.MULTILINE)
+    down_out = re.findall(r"^(osd\.%s) down[ ]+out.*%s:" % (args.osdid, osd_host), output, re.MULTILINE)
+  else:
+    down = re.findall(r"^(osd\.%s) down[ ]+in.*%s:" % (args.osdid, osd_host), output, re.MULTILINE)
+    down_in = down
+    down_out = re.findall(r"^(osd\.%s) down[ ]+out.*%s:" % (args.osdid, osd_host), output, re.MULTILINE)
+
+  if down:
+    print("OSD %s: Down OSD%s on %s: %s" % ('CRITICAL' if len(down)>=args.crit else 'WARNING' ,'s' if len(down)>1 else '', args.host, " ".join(down)))
+    print("Up OSDs: " + " ".join(up))
+    print("Down+In OSDs: " + " ".join(down_in))
+    print("Down+Out OSDs: " + " ".join(down_out))
+    print("| 'osd_up'=%d 'osd_down_in'=%d;;%d 'osd_down_out'=%d;;%d" % (len(up), len(down_in), args.crit, len(down_out), args.crit))
+    if len(down)>=args.crit:
+      return STATUS_ERROR
+    else:
+      return STATUS_WARNING
+
+  if up:
+    print("OSD OK")
+    print("Up OSDs: " + " ".join(up))
+    print("Down+In OSDs: " + " ".join(down_in))
+    print("Down+Out OSDs: " + " ".join(down_out))
+    print("| 'osd_up'=%d 'osd_down_in'=%d;;%d 'osd_down_out'=%d;;%d" % (len(up), len(down_in), args.crit, len(down_out), args.crit))
+    return STATUS_OK
+
+  print("OSD WARN: no OSD.%s found on host %s" % (args.osdid, args.host))
+  return STATUS_WARNING
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/roles/ceph/files/nrpe/check_chrony b/roles/ceph/files/nrpe/check_chrony
new file mode 100755
index 0000000..1d220f4
--- /dev/null
+++ b/roles/ceph/files/nrpe/check_chrony
@@ -0,0 +1,128 @@
+#!/usr/bin/env perl
+#===============================================================================
+#  DESCRIPTION: Icinga2 / Nagios Check for chrony time sync status and offset
+#
+#      OPTIONS: -h : Help
+#               -w [warning threshold in seconds]
+#               -c [critical threshold in seconds]
+#
+# REQUIREMENTS: Chrony, perl version 5.10.1+
+#
+#       AUTHOR: Dennis Ullrich (request@decstasy.de)
+#
+#     BUGS ETC: https://github.com/Decstasy/check_chrony
+#
+#      LICENSE: GPL v3 (GNU General Public License, Version 3)
+#               see https://www.gnu.org/licenses/gpl-3.0.txt
+#===============================================================================
+
+use 5.10.1;
+use strict;
+use warnings;
+use utf8;
+use Getopt::Std;
+
+#
+# Variables
+#
+my $chronyDaemonName = "chronyd";
+my $leapOk = "Normal";
+
+my $rc = 3;
+my $msg= "";
+my $perfdata = "";
+
+#
+# Subroutines
+#
+
+sub help {
+  print "check_chrony [options]
+        -w [warning threshold in seconds]
+        -c [critical threshold in seconds]
+        e.g.: check_chrony -w 0.6 -c 2\n";
+  exit(3);
+}
+
+# Script exit with Nagios / Icinga typical output
+sub _exit {
+  my ( $return, $line ) = @_;
+  my @state = ( "OK", "WARNING", "CRITICAL", "UNKNOWN" );
+  print "$state[$return]: $line\n";
+  exit( $return );
+}
+
+# Checks if a process with $_[0] as name exists
+sub proc_exists {
+  my $PID = `ps -C $_[0] -o pid=`;
+  if ( ${^CHILD_ERROR_NATIVE} == 0 ){
+      return 1;
+  }
+  return 0;
+}
+
+#
+# Options
+#
+
+my %options=();
+getopts( "hw:c:", \%options );
+
+# Check input
+if ( keys %options == 0 || defined $options{h} ){
+  &help;
+}
+
+for my $key ( keys %options ){
+  if ( $options{$key} !~ /^[\d\.]+$/ ){
+    &_exit( 3, "Value of option -$key is not a valid number!" );
+  }
+}
+
+#
+# Check chrony process
+#
+
+&_exit( 2, "$chronyDaemonName is not running!" ) if not &proc_exists( $chronyDaemonName );
+
+#
+# Get tracking data
+#
+
+my $chronyOutput = `chronyc tracking`;
+&_exit( 3, "Chronyc tracking command failed!" ) if ${^CHILD_ERROR_NATIVE} != 0;
+
+my ( $offset, $dir ) = $chronyOutput =~ /(?:System\stime)[^\d]+([\d\.]+)(?:.*?)(fast|slow)/;
+my ( $leap ) = $chronyOutput =~ /(?:Leap)[^\:]+(?::\s+)([\w\h]+)/;
+
+#
+# Check stuff
+#
+
+# Check offset
+if ( $offset >= $options{"c"} ){
+  $rc = 2; # Critical
+}
+elsif ( $offset >= $options{"w"} ){
+  $rc = 1; # Warning
+}
+else {
+  $rc = 0; # Ok
+}
+
+# Prepare offset performace data
+$offset = $dir =~ "slow" ? "-$offset" : "+$offset";
+$msg = sprintf( "Time offset of %+.9f seconds to reference.", $offset);
+$perfdata = sprintf( "|offset=%.9fs;%.9f;%.9f", ${offset}, $options{'w'}, $options{'c'});
+
+# Check leap
+if( $leap !~ $leapOk ){
+  &_exit( 2, "Chrony leap status \"$leap\" is not equal to \"$leapOk\"! $msg $perfdata" );
+}
+
+#
+# Return stuff
+#
+
+&_exit($rc, "$msg $perfdata");
+
diff --git a/roles/pve/files/nrpe/check_smartdisk.sh b/roles/ceph/files/nrpe/check_smartdisk.sh
similarity index 100%
rename from roles/pve/files/nrpe/check_smartdisk.sh
rename to roles/ceph/files/nrpe/check_smartdisk.sh
diff --git a/roles/ceph/files/sudoers b/roles/ceph/files/sudoers
new file mode 100644
index 0000000..5e2f0cd
--- /dev/null
+++ b/roles/ceph/files/sudoers
@@ -0,0 +1 @@
+nagios ALL=(root) NOPASSWD: /usr/sbin/smartctl,/sbin/dmsetup
\ No newline at end of file
diff --git a/roles/ceph/tasks/main.yml b/roles/ceph/tasks/main.yml
index c1af77e..f334f87 100644
--- a/roles/ceph/tasks/main.yml
+++ b/roles/ceph/tasks/main.yml
@@ -1,8 +1,2 @@
-- name: Set NRPE Ceph configuration
-  copy:
-    src: nrpe.cfg
-    dest: /etc/nagios/nrpe.d/95-ceph.cfg
-    owner: root
-    group: root
-    mode: u=rw,g=r,o=r
-  notify: restart-nrpe
+- import_tasks: nrpe.yml
+  tags: nrpe
diff --git a/roles/ceph/tasks/nrpe.yml b/roles/ceph/tasks/nrpe.yml
new file mode 100644
index 0000000..c94df0a
--- /dev/null
+++ b/roles/ceph/tasks/nrpe.yml
@@ -0,0 +1,24 @@
+- name: Set NRPE Ceph configuration
+  copy:
+    src: nrpe.cfg
+    dest: /etc/nagios/nrpe.d/95-ceph.cfg
+    owner: root
+    group: root
+    mode: u=rw,g=r,o=r
+  notify: restart-nrpe
+- name: Copy Ceph NRPE plugins
+  copy:
+    src: nrpe/
+    dest: /etc/nagios/plugins/
+    owner: root
+    group: root
+    mode: u=rwx,g=rx,o=rx
+  notify: restart-nrpe
+- name: Add nagios to sudoers
+  copy:
+    src: sudoers
+    dest: /etc/sudoers.d/nagios
+    mode: u=rw,g=r,o=
+    owner: root
+    group: root
+  notify: restart-nrpe
diff --git a/roles/pve/files/nrpe.cfg b/roles/pve/files/nrpe.d/95-pve.cfg
similarity index 100%
rename from roles/pve/files/nrpe.cfg
rename to roles/pve/files/nrpe.d/95-pve.cfg
diff --git a/roles/pve/files/nrpe/check_chrony b/roles/pve/files/plugins/check_chrony
similarity index 100%
rename from roles/pve/files/nrpe/check_chrony
rename to roles/pve/files/plugins/check_chrony
diff --git a/roles/pve/files/plugins/check_ilo2_health.pl b/roles/pve/files/plugins/check_ilo2_health.pl
new file mode 100755
index 0000000..4f5dba4
--- /dev/null
+++ b/roles/pve/files/plugins/check_ilo2_health.pl
@@ -0,0 +1,1234 @@
+#!/usr/bin/perl
+# icinga: -epn
+
+# check_ilo2_health.pl
+# based on check_stuff.pl and locfg.pl
+#
+# Nagios plugin using the Nagios::Plugin or Monitoring::Plugin module and the
+# HP Lights-Out XML PERL Scripting Sample from
+# ftp://ftp.hp.com/pub/softlib2/software1/pubsw-linux/p391992567/v60711/linux-LOsamplescripts3.00.0-2.tgz
+# checks if all sensors are ok, returns warning on high temperatures and
+# fan failures and critical on overall health failure
+#
+# Alexander Greiner-Baer <alexander.greiner-baer@web.de> 2007 - 2021
+# Matthew Stier <Matthew.Stier@us.fujitsu.com> 2011
+# Claudio Kuenzler <ck@claudiokuenzler.com> 2021
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# 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/>.
+#
+#
+# Changelog:
+# 1.66    Wed, 21 Apr 2021 13:00:00 +0200
+#   support both Nagios::Plugin and Monitoring::Plugin modules
+#   add option "-W" for checking server's current power usage (in Watt)
+# 1.65    Sat, 06 Feb 2021 13:00:03 +0100
+#	  new option "--ignorecacheother|-O"
+#	    ignores cache status "Other"
+# 1.64    Tue, 02 Jun 2020 18:56:30 +0200
+#   fix memory status (was Unknown on an otherwise healthy G8)
+#   fix drive check for ilo2, was broken for 5 years...
+#   new option "-L"
+#     retrieve event log from RIB_INFO section
+#   fix help message for "-l"
+#   new option "-S"
+#     check self_test value from https://<ilohost>/json/login_session
+# 1.63    Tue, 13 Nov 2018 18:41:48 +0100
+#   support iLO5 firmware infos
+#   applied patch from Rene Koch
+#     ignore link unknown (option "-U")
+# 1.62    Mon, 14 May 2018 19:05:22 +0200
+#   retrieve firmware infos only when using --getinfos
+# 1.61    Thu, 01 Jun 2017 20:05:04 +0200
+#   fix for iLO4 2.50 link state when using --ignorelinkdown
+# 1.60    Wed, 12 Aug 2015 18:20:13 +0200
+#   provide --sslopts to override defaults settings
+#   fix }; for GET_EVENT_LOG
+#   applied patch from Rene Koch <rene.koch@siedl.net>:
+#     handle missing values when using "-g"
+#   CONTROLLER_STATUS not present on iLO4 anymore, use STATUS instead
+#   put SSL_VERIFY_NONE in ''
+# 1.59    Wed, 28 Jan 2015 18:56:26 +0100
+#   fix chunk size handling
+#   corrected HTTP/1.1 HOST Header
+#   applied patch from Max Winterstein <winterstein@siriusonline.de>:
+#     sslv3 support
+#   add retries option
+#   catch XMLin() errors
+#   applied patch from Rene Koch <rene.koch@siedl.net>:
+#     ignore battery not installed status (option "-x")
+#     display server name (option "-g")
+#     added warning for logical drive status "Degraded (Recovering)"
+#     display system details (hardware model, serial number, SystemROM, iLO version)
+#     display memory size and part number in case of memory failure
+#     display hard disk model number in case of hard disk failure
+#     display power supply part number in case of power supply failure
+# 1.58    Thu, 08 Aug 2013 18:17:02 +0200
+#   ignore network link down status (option "-i")
+#   added ENCLOSURE_ADDR to drive bay label (bay numbering was inconsistent)
+#   ignore spare drives
+# 1.57    Fri, 17 May 2013 19:30:48 +0200
+#   SSL_verify_mode SSL_VERIFY_NONE (IO::Socket::SSL changed default)
+#   event log support for ilo2
+#   disable embedded perl in icinga
+# 1.56    Fri, 15 Mar 2013 20:47:13 +0100
+#   applied patch from Niklas Edmundsson <Niklas.Edmundsson@hpc2n.umu.se>:
+#     check processor and memory details
+#   applied patches from Dragan Sekerovic <dragan.sekerovic@onestep2.at>:
+#     add location label to temperature (option "-b")
+#     support for checking event log (option "-l")
+#     add iLO version to output
+#   add 2 new values for power supply status
+#   --
+# 1.55    Sun, 05 Aug 2012 20:18:46 +0200
+#   faulty drive (option "-c") exits now with CRITICAL instead of WARNING
+#   applied patches from Niklas Edmundsson <Niklas.Edmundsson@hpc2n.umu.se>:
+#     iLO4 RAID Controller Status
+#     nodriveexit
+#   add g6 drive status
+#   overall health probes every element now
+#   fixed bug with drive bay index
+#   supports iLO3 with multiple backplanes
+#   supports iLO4 disk check
+#   Note: overall health may show drive/storage status, even without "-c"
+#   --
+# 1.54    Thu, 14 Jun 2012 21:36:40 +0200
+#   applied fix for iLO4 from Niklas Edmundsson <Niklas.Edmundsson@hpc2n.umu.se>
+#   --
+# 1.53    Tue, 14 Feb 2012 19:47:40 +0100
+#   added new disk bay variant
+#   added power supply NOT APPLICABLE
+#   --
+# 1.52    Wed, 27 Jul 2011 20:46:14 +0200
+#   fixed <LABEL VALUE = "Power Supplies"/> again
+#   --
+# 1.51    Mon, 25 Jul 2011 19:36:53 +0200
+#   fixed bug with chunked replies by Matthew Stier
+#   --
+# 1.5     Sat, 16 Jul 2011 10:02:10 +0200
+#    optimized by Matthew Stier
+#   --
+# 1.47    Thu, 14 Jul 2011 12:02:01 +0200
+#   also print perfdata when temperature output is disabled
+#   --
+# 1.46    Wed, 06 Jul 2011 08:46:51 +0200
+#   fixed bug with nagios embedded perl interpreter
+#   --
+# 1.45    Wed, 13 Oct 2010 22:17:01 +0200
+#   new option "--ilo3"
+#
+#   "--checkdrives" enhancements
+#
+#   <LABEL VALUE = "Power Supplies"/> shows always "Failed" even when the power
+#   supplies are redundant
+#
+#   improved "--fanredundancy" and "--powerredundancy"
+#   --
+# 1.44    Mon, 14 Dec 2009 20:11:37 +0100
+#   new option "--checkdrives"
+#   --
+# 1.43    Mon, 17 Aug 2009 20:50:13 +0200
+#   new option "--fanredundancy"
+#
+#   new option "--powerredundancy"
+#   --
+# 1.42          Mon, 17 Aug 2009 12:52:23 +0100
+#   check power supply and fans redundancy
+#               gcivitella@enter.it
+#   --
+# 1.41          Thu, 26 Jul 2007 17:42:36 +0200
+#   perfdata label ist now quoted
+#   --
+# 1.4           Mon, 25 Jun 2007 09:45:52 +0200
+#   check vrm and power supply
+#
+#   new option "--notemperatures"
+#
+#   new option "--perfdata"
+#
+#   some minor changes
+#   --
+# 1.3beta       Wed, 20 Jun 2007 09:57:46 +0200
+#   do some error checking
+#
+#   new option "--inputfile"
+#   read bmc output from file
+#   --
+# 1.2   Mon, 18 Jun 2007 09:33:17 +0200
+#   new option "--skipsyntaxerrors"
+#   ignores syntax errors in the xml output, maybe required by older firmwares
+#
+#   introduce a date to the changelog ;)
+#   --
+# 1.1   do not return warning if temperature status is n/a
+#
+#   add "<LOCFG VERSION="2.21" />" to get rid of the
+#   "<INFORM>Scripting utility should be updated to the latest version.</INFORM>"
+#   message
+#   --
+# 1     initial release
+
+use strict;
+use warnings;
+use strict 'refs';
+
+use Sys::Hostname;
+use IO::Socket::SSL;
+use XML::Simple;
+
+$Net::SSLeay::slowly = 5;
+
+use vars qw($VERSION $PROGNAME  $verbose $warn $critical $timeout $result);
+$VERSION = 1.66;
+
+$PROGNAME = "check_ilo2_health";
+
+# instantiate Nagios::Plugin or Monitoring::Plugin
+sub load_module {
+    my @names = @_;
+    my $module;
+    for my $name (@names) {
+        my $file = $name;
+        # requires need either a bare word or a file name
+        $file =~ s{::}{/}gsxm;
+        $file .= '.pm';
+        eval {
+            require $file;
+            $name->import();
+            $module = $name;
+		};
+		last if $module;
+    }
+    return $module;
+}
+
+my $plugin_module;
+BEGIN {
+	$plugin_module = load_module( 'Monitoring::Plugin', 'Nagios::Plugin' );
+}
+
+
+our $p = $plugin_module->new(
+        usage => "Usage: %s [-H <host>] [ -u|--user=<USERNAME> ]
+  [ -p|--password=<PASSWORD> ] [ -f|--inputfile=<filename> ]
+  [ -a|--fanredundancy ] [ -c|--checkdrives ] [ -d|--perfdata ]
+  [ -e|--skipsyntaxerrors ] [ -n|--notemperatures ] [ -3|--ilo3 ]
+  [ -o|--powerredundancy ] [ -b|--locationlabel ] [ -l|--eventlogcheck]
+  [ -i|--ignorelinkdown ] [ -x|--ignorebatterymissing ] [ -s|--sslv3 ]
+  [ -t <timeout> ] [ -r <retries> ] [ -g|--getinfos ] [ --sslopts ]
+  [ -U|--ignorelinkunknown ] [ -L|--eventlogiLO ] [ -S|--iLOselftest ]
+  [ -O|--ignorecacheother ] [ -W|--powerusage ]
+  [ -v|--verbose ] ",
+        version => $VERSION,
+        blurb => 'This plugin checks the health status on a remote iLO2|3|4|5 device
+and will return OK, WARNING or CRITICAL. iLO (integrated Lights-Out)
+can be found on HP Proliant servers.'
+);
+
+$p->add_arg(
+  spec => 'host|H=s',
+  help =>
+  qq{-H, --host=STRING
+  Specify the host on the command line.},
+);
+
+# add all arguments
+$p->add_arg(
+  spec => 'user|u=s',
+  help =>
+  qq{-u, --user=STRING
+  Specify the username on the command line.},
+);
+
+$p->add_arg(
+  spec => 'password|p=s',
+  help =>
+  qq{-p, --password=STRING
+  Specify the password on the command line.},
+);
+
+$p->add_arg(
+  spec => 'inputfile|f=s',
+  help =>
+  qq{-f, --inputfile=STRING
+  Read input from file.},
+);
+
+$p->add_arg(
+  spec => 'fanredundancy|a',
+  help =>
+  qq{-a, --fanredundancy
+  Check fan redundancy},
+);
+
+$p->add_arg(
+  spec => 'checkdrives|c',
+  help =>
+  qq{-c, --checkdrives
+  Check drive bays.},
+);
+
+$p->add_arg(
+  spec => 'perfdata|d',
+  help =>
+  qq{-d, --perfdata
+  Enable perfdata on output.},
+);
+
+$p->add_arg(
+  spec => 'locationlabel|b',
+  help =>
+  qq{-b, --locationlabel
+  Show temperature with location.},
+);
+
+$p->add_arg(
+  spec => 'eventlogcheck|l',
+  help =>
+  qq{-l, --eventlogcheck
+  Parse iLO event log "Integrated Management Log" (SERVER_INFO) for interesting events (f.e. broken memory).},
+);
+
+$p->add_arg(
+  spec => 'eventlogiLO|L',
+  help =>
+  qq{-L, --eventlogiLO
+  Parse iLO event log "iLO Event Log" (RIB_INFO) for interesting events (f.e. Embedded media manager failed media attach).},
+);
+
+$p->add_arg(
+  spec => 'skipsyntaxerrors|e',
+  help =>
+  qq{-e, --skipsyntaxerrors
+  Skip syntax errors on older firmwares.},
+);
+
+$p->add_arg(
+  spec => 'ignorebatterymissing|x',
+  help =>
+  qq{-x, --ignorebatterymissing
+  Ignore Battery missing status.},
+);
+
+$p->add_arg(
+  spec => 'ignorelinkdown|i',
+  help =>
+  qq{-i, --ignorelinkdown
+  Ignore NIC Link Down status (iLO4).},
+);
+
+$p->add_arg(
+  spec => 'ignorelinkunknown|U',
+  help =>
+  qq{-U, --ignorelinkunknown
+  Ignore NIC Link Unknown status (iLO5).},
+);
+
+$p->add_arg(
+  spec => 'notemperatures|n',
+  help =>
+  qq{-n, --notemperatures
+  Disable temperature listing.},
+);
+
+$p->add_arg(
+  spec => 'powerredundancy|o',
+  help =>
+  qq{-o, --powerredundancy
+  Check power redundancy.},
+);
+
+$p->add_arg(
+  spec => 'getinfos|g',
+  help =>
+  qq{-g, --getinfos
+  Display additional infos like firmware version and servername. May need increased timeout.},
+);
+
+$p->add_arg(
+  spec => 'ilo3|3',
+  help =>
+  qq{-3, --ilo3
+  Check iLO version >= 3 device.},
+);
+
+$p->add_arg(
+  spec => 'retries|r=i',
+  help => 
+  qq{-r, --retries=INTEGER
+  Number of retries.},
+);
+
+$p->add_arg(
+  spec => 'sslv3|s',
+  help => 
+  qq{-s, --sslv3
+  Use sslv3 for connection.},
+);
+
+$p->add_arg(
+  spec => 'sslopts=s',
+  help => 
+  qq{--sslopts
+  Sets IO::Socket:SSL Options, defaults to 'SSL_verify_mode => SSL_VERIFY_NONE'.
+  Some firmware may need --sslopts 'SSL_verify_mode => SSL_VERIFY_NONE, SSL_version => "TLSv1"'.},
+);
+
+$p->add_arg(
+  spec => 'selftest|S',
+  help =>
+  qq{-S, --selftest
+  Make additional check for iLO self test from https://<ilohost>/json/login_session. May only work on iLO5.},
+);
+
+$p->add_arg(
+  spec => 'ignorecacheother|O',
+  help =>
+  qq{-O, --ignorecacheother
+  Ignore cache status "Other".},
+);
+
+$p->add_arg(
+  spec => 'powerusage|W',
+  help =>
+  qq{-W, --powerusage
+  Check servers current power usage (in Watt)},
+);
+
+# parse arguments
+$p->getopts;
+
+my $return = "OK";
+my $message = "";
+our $xmlinput = "";
+our $isinput = 0;
+our $drive_input = "";
+our $is_drive_input = 0;
+our $drive_xml_broken = 0;
+our $client;
+our $is_event_input = 0;
+our $event_severity = "";
+our $event_class = "";
+our $event_description = "";
+our %event_status;
+my $host = $p->opts->host;
+my $hostname = $p->opts->host;
+my $username = $p->opts->user;
+my $password = $p->opts->password;
+my $inputfile = $p->opts->inputfile;
+our $skipsyntaxerrors = defined($p->opts->skipsyntaxerrors) ? 1 : 0;
+my $optfanredundancy = defined($p->opts->fanredundancy) ? 1 : 0;
+my $optpowerredundancy = defined($p->opts->powerredundancy) ? 1 : 0;
+my $notemperatures = defined($p->opts->notemperatures) ? 1 : 0;
+our $optcheckdrives = defined($p->opts->checkdrives) ? 1 : 0;
+my $optilo3 = defined($p->opts->ilo3) ? 1 : 0;
+my $iloboardversion = defined($p->opts->ilo3) ? "ILO>=3" : "ILO2";
+my $perfdata = defined($p->opts->perfdata) ? 1 : 0;
+my $locationlabel = defined($p->opts->locationlabel) ? 1 : 0;
+my $eventlogcheck = defined($p->opts->eventlogcheck) ? 1 : 0;
+my $eventlogiLO = defined($p->opts->eventlogiLO) ? 1 : 0;
+my $ignorelinkdown = defined($p->opts->ignorelinkdown) ? 1 : 0;
+my $ignorelinkunknown = defined($p->opts->ignorelinkunknown) ? 1 : 0;
+my $ignorebatterymissing = defined($p->opts->ignorebatterymissing) ? 1 : 0;
+my $optpowerusage = defined($p->opts->powerusage) ? 1 : 0;
+my $getinfos = defined($p->opts->getinfos) ? 1 : 0;
+our %drives;
+our $drive;
+our $drivestatus;
+our $product_name = "";
+our $serial_number = "";
+our $server_name = "";
+my $retries=0;
+my $xml;
+my $sslv3 = defined($p->opts->sslv3) ? 1 : 0;
+my $sslopts = 'SSL_verify_mode => SSL_VERIFY_NONE';
+our @product;
+our @serial;
+our @sname;
+my $optselftest = defined($p->opts->selftest) ? 1 : 0;
+my $ignorecacheother = defined($p->opts->ignorecacheother) ? 1 : 0;
+
+$message = "(Board-Version: $iloboardversion) ";
+
+unless ( ( defined($inputfile) ) ||
+         ( defined($host) && defined($username) && defined($password) ) ) {
+  $p->nagios_die("ERROR: Missing host, password and user.");
+}
+
+if ( defined ( $p->opts->retries ) ) {
+  $retries = $p->opts->retries;
+}
+
+if ( defined ( $p->opts->sslopts ) ) {
+  $sslopts = $p->opts->sslopts;
+}
+
+alarm $p->opts->timeout;
+
+my $boundary;
+our $sendsize;
+my $localhost = hostname() || 'localhost';
+print "hostname is $localhost\n" if ( $p->opts->verbose );
+
+for (my $i=0;$i<=$retries;$i++) {
+  print "retry: $i\n" if ( $p->opts->verbose );
+  unless ( defined($inputfile) ) {
+    # query code from locfg.pl
+    # Set the default SSL port number if no port is specified
+    $host .= ":443" unless ($host =~ m/:/);
+    #
+    # Open the SSL connection and the input file
+    $client = new IO::Socket::SSL->new(PeerAddr => $host, eval $sslopts, $sslv3 ? 
+    ( SSL_version => 'SSLv3' ) : () );
+    unless ( $client ) {
+      $p->nagios_exit(
+        return_code => "UNKNOWN",
+        message => "ERROR: Failed to establish SSL connection with $host $! $SSL_ERROR."
+      );
+    }
+
+    if ( $optilo3 ) {
+      print "sending ilo3\n" if ( $p->opts->verbose );
+      my $cmd = '<?xml version="1.0"?>';
+      $cmd .= '<LOCFG VERSION="2.21" />';
+      $cmd .= '<RIBCL VERSION="2.21">';
+      $cmd .= '<LOGIN USER_LOGIN="'.$username.'" PASSWORD="'.$password.'">';
+      $cmd .= '<SERVER_INFO MODE="read">';
+      $cmd .= '<GET_EMBEDDED_HEALTH />';
+      if ( $eventlogcheck ) { 
+        $cmd .= '<GET_EVENT_LOG />';
+      }
+      if ( $getinfos ) {
+        $cmd .= '<GET_HOST_DATA />';
+        $cmd .= '<GET_PRODUCT_NAME />';
+        $cmd .= '<GET_SERVER_NAME />';
+      }
+      $cmd .= '</SERVER_INFO>';
+      if ( $eventlogiLO ) {
+        $cmd .= '<RIB_INFO MODE="read">';
+        $cmd .= '<GET_EVENT_LOG />';
+        $cmd .= '</RIB_INFO>';
+      }
+      $cmd .= '</LOGIN>';
+      $cmd .= '</RIBCL>';
+      $cmd .= "\r\n";
+      send_or_calculate(0,$cmd);
+
+      send_to_client(0, "POST /ribcl HTTP/1.1\r\n");
+      send_to_client(0, "HOST: $hostname\r\n");          # Mandatory for http 1.1
+      send_to_client(0, "TE: chunked\r\n");
+      send_to_client(0, "Connection: Close\r\n");         # Required
+      send_to_client(0, "Content-length: $sendsize\r\n"); # Mandatory for http 1.1
+      send_to_client(0, "\r\n");
+      send_or_calculate(1,$cmd);  #Send it to iLO
+    }
+    else {
+      # send xml to BMC
+      print $client '<?xml version="1.0"?>' . "\r\n";
+      print $client '<LOCFG VERSION="2.21" />' . "\r\n";
+      print $client '<RIBCL VERSION="2.21">' . "\r\n";
+      print $client '<LOGIN USER_LOGIN="'.$username.'" PASSWORD="'.$password.'">' . "\r\n";
+      print $client '<SERVER_INFO MODE="read">' . "\r\n";
+      print $client '<GET_EMBEDDED_HEALTH />' . "\r\n";
+      if ( $eventlogcheck ) { 
+        print $client '<GET_EVENT_LOG />' . "\r\n"; 
+      }
+      if ( $getinfos ) {
+        print $client '<GET_HOST_DATA />' . "\r\n";
+        print $client '<GET_PRODUCT_NAME />' . "\r\n";
+        print $client '<GET_SERVER_NAME />' . "\r\n";
+      }
+      print $client '</SERVER_INFO>' . "\r\n";
+      if ( $eventlogiLO ) {
+        print $client '<RIB_INFO MODE="read">' . "\r\n";
+        print $client '<GET_EVENT_LOG />' . "\r\n";
+        print $client '</RIB_INFO>' . "\r\n";
+      }
+      print $client '</LOGIN>' . "\r\n";
+      print $client '</RIBCL>' . "\r\n";
+    }
+  }
+  else {
+    open($client,$inputfile) or $p->nagios_die("ERROR: $inputfile not found");
+  }
+
+# retrieve data
+  if ( $optilo3 && !$inputfile ) {
+    read_chunked_reply();
+  }
+  else {
+    while (my $ln = <$client>) {
+      parse_reply($ln);
+    }
+    close $client;
+  }
+
+# parse with XML::Simple
+  if ( $xmlinput && $isinput == 0 ) {
+    $xml = eval { XMLin($xmlinput, ForceArray => 1) };
+    if ( $@ ) {
+      if ( $i < $retries ) { 
+        next;
+      }
+      $p->nagios_exit(
+        return_code => "UNKNOWN",
+        message => "ERROR: $@"
+      );
+    }
+    else {
+      last;
+    }
+  }
+  else {
+    $p->nagios_exit(
+      return_code => "UNKNOWN",
+      message => "ERROR: No parseable output."
+    );
+  }
+}
+
+if ( $getinfos ) {
+  $serial_number = "";
+  if ( defined $serial[3] ) {
+    $serial[3] =~ tr/ //ds ;
+    $serial_number = "Serial: $serial[3]";
+  }
+  $server_name = "";
+  $server_name = " - Servername: $sname[1]" if defined $sname[1];
+  my $system_rom = undef;
+  my $firmware_name = undef;
+  my $firmware_version = undef;
+
+  # loop through firmware hash
+  foreach my $index (keys %{ $xml->{'FIRMWARE_INFORMATION'}[0] }) {
+    if (defined $xml->{'FIRMWARE_INFORMATION'}[0]->{$index}[0]->{'FIRMWARE_NAME'}[0]->{'VALUE'}) {
+      if ($xml->{'FIRMWARE_INFORMATION'}[0]->{$index}[0]->{'FIRMWARE_NAME'}[0]->{'VALUE'} =~ m/^iLO/) {
+        $firmware_name    = $xml->{'FIRMWARE_INFORMATION'}[0]->{$index}[0]->{'FIRMWARE_NAME'}[0]->{'VALUE'};
+        $firmware_version = $xml->{'FIRMWARE_INFORMATION'}[0]->{$index}[0]->{'FIRMWARE_VERSION'}[0]->{'VALUE'};
+      } elsif ($xml->{'FIRMWARE_INFORMATION'}[0]->{$index}[0]->{'FIRMWARE_NAME'}[0]->{'VALUE'} eq "HP ProLiant System ROM" || $xml->{'FIRMWARE_INFORMATION'}[0]->{$index}[0]->{'FIRMWARE_NAME'}[0]->{'VALUE'} eq "System ROM") {
+        $system_rom = $xml->{'FIRMWARE_INFORMATION'}[0]->{$index}[0]->{'FIRMWARE_VERSION'}[0]->{'VALUE'};
+      }
+    }
+  }
+
+  my $product_name = undef;
+  if (! defined $product[1]) {
+    $product_name = "Unknown product";
+  } else {
+    $product_name = $product[1]
+  }
+  $serial_number = "Unknown serial" if ! defined $serial_number;
+  $firmware_name = "iLO" if ! defined $firmware_name;
+  $firmware_version = "Unknown firmware version" if ! defined $firmware_version;
+  $server_name = "Unknown server name" if ! defined $server_name;
+
+  if (defined $system_rom) {
+    $message = "($product_name - SystemROM: $system_rom - $serial_number - $firmware_name FW $firmware_version" . "$server_name) ";
+  } else {
+    $message = "($product_name - $serial_number - $firmware_name FW $firmware_version" . "$server_name) ";
+  }
+}
+
+my $drive_xml;
+if ( $optcheckdrives && !$drive_xml_broken ) {
+  if ( $drive_input && $is_drive_input == 0 ) {
+    $drive_xml = eval { XMLin($drive_input, ForceArray => 1) };
+    if ( $@ ) {
+      $p->nagios_exit(
+        return_code => "UNKNOWN",
+        message => "ERROR: $@"
+      );
+    }
+  }
+  elsif ( ref $xml->{'STORAGE'}[0]->{'CONTROLLER'} ) {
+    # iLO4 specific, no need for $drive_input
+  }
+  else {
+    # No need to error out if host uncapable of checking drive status
+    warn "No drive_input found" if ( $p->opts->verbose );
+  }
+}
+
+my $temperatures = $xml->{'TEMPERATURE'}[0]->{'TEMP'};
+my $backplanes = $drive_xml->{'BACKPLANE'};
+my $raidcontroller = $xml->{'STORAGE'}[0]->{'CONTROLLER'};
+my @checks;
+push(@checks,$xml->{'FANS'}[0]->{'FAN'});
+push(@checks,$xml->{'VRM'}[0]->{'MODULE'});
+push(@checks,$xml->{'POWER_SUPPLIES'}[0]->{'SUPPLY'});
+if($xml->{'PROCESSORS'}) {
+        push(@checks,$xml->{'PROCESSORS'}[0]->{'PROCESSOR'});
+}
+my $memdetails;
+if($xml->{'MEMORY'}) {
+        $memdetails = $xml->{'MEMORY'}[0]->{'MEMORY_DETAILS'}[0];
+}
+my $health = $xml->{'HEALTH_AT_A_GLANCE'}[0];
+my $label;
+my $status;
+my $temperature;
+my $cautiontemp;
+my $criticaltemp;
+
+## check overall health status
+
+my $componentstate;
+foreach (keys %{$health}) {
+  $componentstate = $health->{$_}[0]->{'STATUS'};
+  if ( defined($componentstate) && ( $componentstate !~ m/^Ok$|^OTHER$|^NOT APPLICABLE$/i ) ) {
+    if ($_ eq 'STORAGE') {
+      if ( ref($raidcontroller) ) {
+       # For iLO4 we can look at the raid controller to get a more detailed
+       # status, so just log a WARNING unless we find something CRITICAL
+       # later on.
+       $return = "WARNING" unless ( $return eq "CRITICAL" );
+      }
+      else {
+       $return = "CRITICAL";
+      }
+    }
+    elsif ( ( $_ eq 'BATTERY' ) && $ignorebatterymissing && ( $componentstate =~ m/^Not Installed$/i ) ) {
+      next;
+    }
+    elsif ( ( $_ eq 'NETWORK' ) && $ignorelinkdown && ( $componentstate =~ m/^Link Down$/i || $componentstate =~ m/^Degraded$/i ) ) {
+      next;
+    }
+    elsif ( ( $_ eq 'NETWORK' ) && $ignorelinkunknown && ( $componentstate =~ m/^Unknown$/i ) ) {
+      next;
+    }
+    else {
+      $return = "CRITICAL";
+    }
+    $message .= "$_ $componentstate, ";
+  }
+}
+
+if ( $optpowerredundancy ) {
+  my $powerredundancy = $health->{'POWER_SUPPLIES'}[1]->{'REDUNDANCY'};
+  if ( defined($powerredundancy) &&
+    ( $powerredundancy !~ m/^Fully Redundant$|^REDUNDANT$|^NOT APPLICABLE$/i ) ) {
+    $return = "CRITICAL";
+    $message .= "Power supply $powerredundancy, ";
+  }
+}
+
+if ( $optpowerusage ) {
+  my $powerusage=$xml->{'POWER_SUPPLIES'}[0]->{'POWER_SUPPLY_SUMMARY'}[0]->{'PRESENT_POWER_READING'}[0]->{'VALUE'};
+  my ($powerperf) = $powerusage =~ m/(\d+) [Ww]atts/i;
+  $message .= "Power Usage: $powerusage, ";
+  if ( $perfdata ) {
+    $p->add_perfdata(
+      label   => "power",
+      value   => $powerperf,
+      uom     => ""
+      );
+  }
+}
+
+if ( $optfanredundancy ) {
+  my $fanredundancy = $health->{'FANS'}[1]->{'REDUNDANCY'};
+  if ( defined($fanredundancy) &&
+    ( $fanredundancy !~ m/^Fully Redundant$|^REDUNDANT$|^NOT APPLICABLE$/i ) ) {
+    $return = "CRITICAL";
+    $message .= "Fans $fanredundancy, ";
+  }
+}
+
+# check fans, vrm and power supplies
+foreach my $check ( @checks ) {
+  if ( ref($check) ) {
+    foreach my $item ( @$check ) {
+      $label=$item->{'LABEL'}[0]->{'VALUE'};
+      $status=$item->{'STATUS'}[0]->{'VALUE'};
+      if ( defined($label) && defined($status) ) {
+        # misleading output on some iLO3 shows always failed, skip it
+        if ($label =~ m/^Power Supplies$/) {
+          next;
+        }
+        if ($label =~ m/^Power Supply/) {
+          # get details for power supplies
+          $label =~ s/ /_/g;
+          if ( ( $status !~ m"^Ok$|^Good|^n/a$|^Not Installed$|^Unknown$"i ) ) {
+            $return = "WARNING" unless ( $return eq "CRITICAL" );
+            if ( defined($item->{'MODEL'}[0]->{'VALUE'}) ) {
+              $message .= "$label is $status (ModelNumber: $item->{'MODEL'}[0]->{'VALUE'}) ";
+            }
+            else {
+              $message .= "$label is $status ";
+            }
+          }
+          next;
+        }
+        $label =~ s/ /_/g;
+        if ( ( $status !~ m"^Ok$|^Good|^n/a$|^Not Installed$|^Unknown$"i ) ) {
+          $return = "WARNING" unless ( $return eq "CRITICAL" );
+          $message .= "$label: $status, ";
+        }
+      }
+    }
+  }
+}
+
+# check memory status (iLO4 only?)
+if ( ref($memdetails) ) {
+  foreach my $loc ( sort keys %{$memdetails} ) {
+    foreach ( @{$memdetails->{$loc}} ) {
+      $status = $_->{'STATUS'}[0]->{'VALUE'};
+      if ( ( $status !~ m"^Ok$|^Good|^n/a$|^Not Present$|^Unknown$"i ) ) {
+        $return = "WARNING" unless ( $return eq "CRITICAL" );
+        my $socket = $_->{'SOCKET'}[0]->{'VALUE'};
+        my $size = $_->{'SIZE'}[0]->{'VALUE'};
+        my $part = $_->{'PART'}[0]->{'NUMBER'};
+        if ( defined $part ) {
+          # works only with new iLO4 firmware
+          $message .= "Mem $loc $socket: $status (Size: $size, PartNumber: $part), ";
+        }
+        else {
+          $message .= "Mem $loc $socket: $status (Size: $size), ";
+        }
+      }
+    }
+  }
+}
+
+# check newer drive bays (iLO3)
+if ( ref($backplanes) ) {
+  my $backplane = 0;
+  foreach ( @{$backplanes} ) {
+    if ( defined($_->{'ENCLOSURE_ADDR'}[0]->{'VALUE'} ) ) {
+      $backplane = $_->{'ENCLOSURE_ADDR'}[0]->{'VALUE'};
+    }
+    else {
+      $backplane++;
+    }
+    if ( $_->{'DRIVE_BAY'} ) {
+      for ( my $i=0; $i<= $#{$_->{'DRIVE_BAY'}}; $i++ ) {
+        $label=$backplane." ".$_->{'DRIVE_BAY'}[$i]->{'VALUE'};
+        $status=$_->{'STATUS'}[$i]->{'VALUE'};
+        $drives{$label}{'status'} = $status;
+      }
+    }
+    if ( $_->{'DRIVE'} ) {
+      for ( my $i=0; $i<= $#{$_->{'DRIVE'}}; $i++ ) {
+        $label=$backplane." ".$_->{'DRIVE'}[$i]->{'BAY'};
+        $status=$_->{'DRIVE_STATUS'}[$i]->{'VALUE'};
+        $drives{$label}{'status'} = $status;
+      }
+    }
+  }
+}
+
+# seems that iLO4 reads the state from the RAID controller, nice
+if ( ref($raidcontroller) ) {
+  foreach ( @{$raidcontroller} ) {
+    my $ctrllabel = $_->{'LABEL'}[0]->{'VALUE'};
+    my $ctrlstatus = $_->{'CONTROLLER_STATUS'}[0]->{'VALUE'} || $_->{'STATUS'}[0]->{'VALUE'};
+    if($ctrlstatus ne 'OK') {
+      $return = "CRITICAL";
+      $message .= "SmartArray $ctrllabel Status: $ctrlstatus, ";
+    }
+    my $cachestatus = $_->{'CACHE_MODULE_STATUS'}[0]->{'VALUE'};
+    if($cachestatus && $cachestatus ne 'OK') {
+      # FIXME: There are probably other valid cache module states that
+      #        needs to be excluded.
+      if($ignorecacheother && $cachestatus eq 'Other') {
+        next;
+      }
+      $return = "CRITICAL";
+      $message .= "SmartArray $ctrllabel Cache Status: $cachestatus, ";
+    }
+    foreach ( @{$_->{'DRIVE_ENCLOSURE'}} ) {
+      my $enclabel = $_->{'LABEL'}[0]->{'VALUE'};
+      my $encstatus = $_->{'STATUS'}[0]->{'VALUE'};
+      my $encmodel = $_->{'MODEL_NUMBER'}[0]->{'VALUE'};
+      if($encstatus ne 'OK') {
+              $message .= "SmartArray $ctrllabel Enclosure $enclabel: $encstatus (ModelNumber: $encmodel) - check hardware status in OS, ";
+        $return = "CRITICAL";
+      }
+    }
+    foreach ( @{$_->{'LOGICAL_DRIVE'}} ) {
+      my $ldlabel = $_->{'LABEL'}[0]->{'VALUE'};
+      my $ldstatus = $_->{'STATUS'}[0]->{'VALUE'};
+      if($ldstatus ne 'OK') {
+              $message .= "SmartArray $ctrllabel LD $ldlabel: $ldstatus, ";
+              if($ldstatus eq 'Degraded (Rebuilding)' || $ldstatus eq 'Degraded (Recovering)') {
+                $return = "WARNING" unless ( $return eq "CRITICAL" );
+              }
+              else {
+                $return = "CRITICAL";
+              }
+      }
+      foreach ( @{$_->{'PHYSICAL_DRIVE'}} ) {
+        $label = "$ctrllabel $_->{'LABEL'}[0]->{'VALUE'}";
+        $status = $_->{'STATUS'}[0]->{'VALUE'};
+        my $model = $_->{'MODEL'}[0]->{'VALUE'};
+        $drives{$label}{'status'} = $status;
+        $drives{$label}{'model'} = $model;
+      }
+    }
+  }
+}
+
+# check drive bays
+if ( $optcheckdrives ) {
+  foreach ( sort keys(%drives) ) {
+    if ( ( $drives{$_}{'status'} !~ m"^(Ok)$|^(n/a)$|^(Spare)$|^(Not Installed)|^(Not Present/Not Installed)$|^(spun down)$"i ) ) {
+      $return = "CRITICAL";
+      $message .= "$_: ".$drives{$_}{'status'};
+      if (defined $drives{$_}{'model'}){
+        $message .= " (Drive ModelNumber: " . $drives{$_}{'model'} ."), ";
+      }
+    }
+  }
+}
+
+# check event logs
+if ( $eventlogcheck || $eventlogiLO ) {
+  foreach ( keys %event_status ) {
+    next if ( $event_status{$_} =~ m/Repaired/ );
+    $message .= " $_:$event_status{$_} ";
+    $return = "WARNING" unless ( $return eq "CRITICAL" );
+  }
+}
+
+# check self test
+if ( $optselftest ) {
+  my %ret = getselfteststatus();
+  if ($ret{'status'}) {
+    $return = "CRITICAL";
+    $message .= "iLO self test is $ret{'selftest'}, ";
+    if ( defined($ret{'faults'}) ) {
+      $message .= "self test fault is $ret{'faults'}, ";
+    }
+  }
+}
+
+
+unless ( $message ) {
+  $message .= "No faults detected, ";
+}
+
+# check temperatures
+if ( ref($temperatures) ) {
+  unless ( $notemperatures ) {
+    $message .= "Temperatures: ";
+  }
+  foreach my $temp ( @$temperatures ) {
+    $label=$temp->{'LABEL'}[0]->{'VALUE'};
+    if ( $locationlabel && defined($temp->{'LOCATION'}[0]->{'VALUE'}) ) {
+      $label .= " (" . $temp->{'LOCATION'}[0]->{'VALUE'} . ")";
+    }
+    $status=$temp->{'STATUS'}[0]->{'VALUE'};
+    $temperature=$temp->{'CURRENTREADING'}[0]->{'VALUE'};
+    if ( defined($label) && defined($status) && defined($temperature) ) {
+      $label =~ s/ /_/g;
+      unless ( ( $status =~ m"^Ok$|^n/a$|^Not Installed$"i ) ) {
+        $return = "WARNING" unless ( $return eq "CRITICAL" );
+        $message .= "$label ($status): $temperature, "
+          if ( $notemperatures );
+      }
+      unless ( ( $status =~ m"^n/a$|^Not Installed$"i ) )  {
+        $message .= "$label ($status): $temperature, "
+          unless ( $notemperatures );
+        if ( $perfdata ) {
+          $cautiontemp=$temp->{'CAUTION'}[0]->{'VALUE'};
+          $criticaltemp=$temp->{'CRITICAL'}[0]->{'VALUE'};
+          # Returned value can be 'N/A', enforce this being a number
+          if($cautiontemp && $cautiontemp !~ /^[0-9]+/) {
+                $cautiontemp=undef;
+          }
+          if($criticaltemp && $criticaltemp !~ /^[0-9]+/) {
+                $criticaltemp=undef;
+          }
+          if ( defined($cautiontemp) && defined($criticaltemp) ) {
+            $p->set_thresholds(
+              warning  => $cautiontemp,
+              critical => $criticaltemp,
+            );
+            my $threshold = $p->threshold;
+            # add perfdata
+            $p->add_perfdata(
+              label   => $label,
+              value   => $temperature,
+              uom     => "",
+              threshold => $threshold,
+            );
+          }
+        }
+      }
+    }
+    else {
+      $message .= "no reading, ";
+    }
+  }
+}
+
+
+# strip trailing ","
+$message =~ s/, $//;
+
+$p->nagios_exit(
+  return_code => $return,
+  message => $message
+);
+
+
+# send_to_client, send_or_calculate and read_chunked_reply
+# are adapted from locfg.pl
+
+sub send_to_client
+{
+  my ($send, $cmd) = @_;
+  print $cmd if ( $p->opts->verbose && length($cmd) < 1024 );
+  print $client $cmd;
+  $sendsize -= length($cmd) if ( $send );
+}
+
+sub send_or_calculate    # used for iLO 3 only
+{
+  $sendsize = 0;
+  my ($send, $cmd) = @_;
+  if ($send) {
+    print $client $cmd;
+  }
+  $sendsize += length($cmd);
+  print "size $sendsize\n" if ( $p->opts->verbose );
+}
+
+
+sub read_chunked_reply    # used for iLO 3 only
+{
+  my $ln = "";
+  my $lp = "";
+  my $hide = 1;
+  my $chunk = 1;
+  my $chunkSize;
+
+  while( 1 ) {
+    # Read a line
+    $ln = <$client>;
+    # Get length of line
+    my $length =  length($ln);
+    # Exit loop if zero
+    if ( $length == 0 ) {
+      if ( $p->opts->verbose ) {
+        print "read_chunked_reply: read a zero-length line. Continue...\n";
+      }
+      last;
+    }
+    # Skip HTTP headers and first line of chunked responses
+    if ( $hide ) {
+        $hide = 0 if ( $ln =~ m/^\r\n$/ );
+        print "Head: " . $ln if ( $p->opts->verbose );
+        next;
+    }
+    # Get size of chunk
+    if ( $chunk ) {
+      print "chunk: " . $ln if ( $p->opts->verbose );
+      $ln =~ s/\r|\n//g;
+      $chunkSize = hex($ln);
+      $chunk = 0;
+      print "chunk size: $chunkSize\n" if ( $p->opts->verbose );
+      next;
+    }
+    # Last Chunk
+    if ( $chunkSize == 0 ) {
+      print "read_chunked_reply: reach end of responses.\n" if ($p->opts->verbose);
+      last;
+    }
+    # End of chunk, process incomplete line
+    if ( $chunkSize < $length ) {
+      $chunk = 1; # Next line, new chunk
+      $hide = 0;  # Skip hide
+      $lp .= substr($ln, 0, $chunkSize); # Truncate and append
+    }
+    # End of chunk, process complete line
+    elsif ( $chunkSize == $length ) {
+      $chunk = 1; # Next line, new chunk
+      $hide = 1;  # Hide new chunk's first line
+      $lp .= $ln; # Append line as-is
+    }
+    # Process line
+    else {
+      $chunkSize -= $length; # Decrement chunk size
+      $lp .= $ln; # Append line as-is
+    }
+    # Skip incomplete line
+    next unless ( $lp =~ m/\n$/ );
+    # Parse complete line
+    parse_reply($lp);
+    # Line parsed, clear line
+    $lp = "";
+  }
+  if ($client->error()) {
+     print "Error: connection error " . $client->error() . "\n";
+  }
+}
+
+sub parse_reply
+{
+  my ($line) = @_;
+  $line =~ s/\r\n$/\n/;
+  print $line if ( $p->opts->verbose );
+
+  if ( $getinfos ) {
+    # Prune all unnecessary lines
+    $isinput = 1 if ( $line =~ m"<GET_EMBEDDED_HEALTH_DATA>|</DRIVES>" );
+    $xmlinput .= $line if ( $isinput );
+    $isinput = 0 if ( $line =~ m"</GET_EMBEDDED_HEALTH_DATA>|<DRIVES>" );
+    $product_name = $line if ( $line =~ m"<PRODUCT_NAME VALUE" );
+    $serial_number = $line if ( $line =~ m/FIELD NAME="Serial Number"/ );
+    $server_name = $line if ( $line =~ m"<SERVER_NAME" );
+    @product = split (/"/, $product_name)  if defined $product_name;
+    @serial  = split (/"/, $serial_number) if defined $serial_number;
+    @sname   = split (/"/, $server_name )  if defined $server_name;
+  }
+  else {
+    # Prune all unnecessary lines
+    $isinput = 1 if ( $line =~ m"<GET_EMBEDDED_HEALTH_DATA>|</DRIVES>|</FIRMWARE_INFORMATION>" );
+    $xmlinput .= $line if ( $isinput );
+    $isinput = 0 if ( $line =~ m"</GET_EMBEDDED_HEALTH_DATA>|<DRIVES>|<FIRMWARE_INFORMATION>" );
+  }
+
+  # drive check needs special handling
+  # <DRIVES>
+  #    <BACKPLANE>
+  #       <FIRMWARE_VERSION VALUE="1.18"/>
+  #       <ENCLOSURE_ADDR VALUE="224"/>
+  #     <DRIVE_BAY VALUE = "1"/>
+  #       <PRODUCT_ID VALUE = "EH0300FBQDD    "/>
+  #       <STATUS VALUE = "Ok"/>
+  #       <UID_LED VALUE = "Off"/>
+  #     <DRIVE_BAY VALUE = "2"/>
+  #       <PRODUCT_ID VALUE = "EH0300FBQDD    "/>
+  #       <STATUS VALUE = "Fault"/>
+  #       <UID_LED VALUE = "Off"/>
+  #    </BACKPLANE>
+  # </DRIVES>
+
+  $is_drive_input = 1 if ( $line =~ m"<DRIVES>" );
+  $drive_input .= $line if ( $is_drive_input );
+  $is_drive_input = 0 if ( $line =~ m"</DRIVES>" );
+
+  # because on many (older?) iLOs drive status is not XML
+  if ($optcheckdrives) {
+    if ( $line =~ m/<Drive Bay: / ) {
+      $drive_xml_broken = 1;
+      # <Drive Bay: "3"; status: "Smart Error"; uid led="Off"/>
+      ( $drive, $drivestatus ) = ( $line =~
+        m/Drive Bay: "(.*)"; status: "(.*)"; uid led: ".*"/ );
+      if ( defined($drive) && defined($drivestatus) ) {
+        $drives{$drive}{'status'} = $drivestatus;
+      }
+    }
+    if ( $line =~ m/<DRIVE BAY=".*" PRODUCT_ID="/ ) {
+      $drive_xml_broken = 1;
+      # <DRIVE BAY="3" PRODUCT_ID="N/A"STATUS="Smart Error" UID_LED="Off"/>
+      ( $drive, $drivestatus ) = ( $line =~
+        m/DRIVE BAY="(.*)" PRODUCT_ID=".*"STATUS="(.*)" UID_LED=".*"/ );
+      if ( defined($drive) && defined($drivestatus) ) {
+        $drives{$drive}{'status'} = $drivestatus;
+      }
+    }
+  }
+
+  if ( $eventlogcheck || $eventlogiLO ) {
+    $is_event_input = 1 if ( $line =~ m"<EVENT" );
+    if ( $is_event_input ) {
+      if ( $line =~ m/SEVERITY="(.*?)"/ ) {
+        $event_severity = $1;
+        #print "SEV: $event_severity\n";
+      }
+      if ( $line =~ m/CLASS="(.*?)"/ ) {
+        $event_class = $1;
+        #print "CLASS: $event_class\n";
+      }
+      if ( $line =~ m/DESCRIPTION="(.*?)"/ ) {
+        $event_description = $1;
+        #print "DESCRIPTION: $event_description\n";
+      }
+    }
+    $is_event_input = 0 if ( $is_event_input && $line =~ m"/>" );
+    if ( $is_event_input == 0 && $event_class ) {
+      if ( ($event_class !~ m/POST|Maintenance/) && ( $event_severity !~ m/Informational/) ) {
+        $event_status{$event_description} = $event_severity;
+        $event_class = "";
+      }
+    }
+  }
+
+  if ( $line =~ m/MESSAGE='(.*)'/ ) {
+    my $msg = $1;
+
+    if ( $msg =~ m/No error/i ) {
+      # Skip
+    }
+    elsif ( $msg =~ m/Syntax error/i && $skipsyntaxerrors ) {
+      # Skip
+    }
+    else {
+      close $client;
+      $p->nagios_exit(
+        return_code => "UNKNOWN",
+        message => "ERROR: $msg."
+      );
+    }
+  }
+}
+
+sub getselfteststatus {
+  my ($json, $selftest, $selftestfaults, $selfteststatus);
+  # Set the default SSL port number if no port is specified
+  $host .= ":443" unless ($host =~ m/:/);
+  # Open the SSL connection and the input file
+  $client = new IO::Socket::SSL->new(PeerAddr => $host, eval $sslopts, $sslv3 ? 
+    ( SSL_version => 'SSLv3' ) : () );
+  unless ( $client ) {
+    $p->nagios_exit(
+      return_code => "UNKNOWN",
+      message => "ERROR: Failed to establish SSL connection with $host $! $SSL_ERROR."
+    );
+  }
+
+  print "sending self test status request\n" if ( $p->opts->verbose );
+
+  send_to_client(0, "GET /json/login_session HTTP/1.1\r\n");
+  send_to_client(0, "HOST: $hostname\r\n");          # Mandatory for http 1.1
+  send_to_client(0, "Connection: Close\r\n");         # Required
+  send_to_client(0, "\r\n");
+
+  # retrieve data
+  while (my $ln = <$client>) {
+    $json .= $ln;
+  }
+  close $client;
+
+  if ( $json ) {
+    print "$json\n" if ( $p->opts->verbose );
+    ( $selftest ) = ( $json =~ m/"self_test":"([^"]+)"/ );
+    ( $selftestfaults ) = ( $json =~ m/"self_test_faults":"([^"]+)"/ );
+    $selfteststatus = 0;
+    if ( defined($selftest) ) {
+      unless ( $selftest =~ m/^OP_STATUS_OK$/ ) {
+        $selfteststatus = 1;
+      }
+    }
+    return ( 'selftest' => $selftest, 'faults' => $selftestfaults, 'status' => $selfteststatus);
+  }
+  else {
+    $p->nagios_exit(
+      return_code => "UNKNOWN",
+      message => "ERROR: No parseable output from self test."
+    );
+  }
+}
diff --git a/roles/pve/files/plugins/check_smartdisk.sh b/roles/pve/files/plugins/check_smartdisk.sh
new file mode 100755
index 0000000..605ea12
--- /dev/null
+++ b/roles/pve/files/plugins/check_smartdisk.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+# Checks status of disks SMART
+
+STATUS_LABEL="SMART Health Status:"
+STATUS_OK="$STATUS_LABEL OK"
+
+if [[ "$#" == "0" ]]; then
+	echo "Usage: $0 <disk1> [<disk2> ... <diskX>]"
+	exit
+fi
+
+for DISK in "$@"
+do
+	STATUS=$(sudo /usr/sbin/smartctl -H -d scsi "$DISK" | grep "$STATUS_LABEL")
+
+	if [ "$STATUS" != "$STATUS_OK" ]; then
+		echo "CRITICAL: $DISK: $STATUS"
+		exit 2
+	fi
+done
+
+echo "OK: $STATUS_OK"
diff --git a/roles/pve/files/nrpe/check_zfs.pl b/roles/pve/files/plugins/check_zfs.pl
similarity index 100%
rename from roles/pve/files/nrpe/check_zfs.pl
rename to roles/pve/files/plugins/check_zfs.pl
diff --git a/roles/pve/tasks/nrpe.yml b/roles/pve/tasks/nrpe.yml
index e280c13..f242d49 100644
--- a/roles/pve/tasks/nrpe.yml
+++ b/roles/pve/tasks/nrpe.yml
@@ -1,14 +1,20 @@
+- name: Install base packages
+  apt:
+    name:
+      - libxml-simple-perl
+      - libmonitoring-plugin-perl
+    state: present
 - name: Set NRPE PVE configuration
   copy:
-    src: nrpe.cfg
-    dest: /etc/nagios/nrpe.d/95-pve.cfg
+    src: nrpe.d/95-pve.cfg
+    dest: /etc/nagios/nrpe.d/
     owner: root
     group: root
     mode: u=rw,g=r,o=r
   notify: restart-nrpe
 - name: Copy PVE NRPE plugins
   copy:
-    src: nrpe/
+    src: plugins/
     dest: /etc/nagios/plugins/
     owner: root
     group: root
diff --git a/roles/pve/tasks/vhost.yml b/roles/pve/tasks/vhost.yml
index 77c25b5..3287de5 100644
--- a/roles/pve/tasks/vhost.yml
+++ b/roles/pve/tasks/vhost.yml
@@ -7,8 +7,8 @@
     group: root
   register: copy_result
 
-- name: Reboot the system if file was copied
-  reboot:
-    reboot_timeout: 600
-  become: true
-  when: copy_result.changed
\ No newline at end of file
+#- name: Reboot the system if file was copied
+#  reboot:
+#    reboot_timeout: 600
+#  become: true
+#  when: copy_result.changed
\ No newline at end of file

From 2cbf9c561930cb8cbf8d3eee9da9cd1f08833879 Mon Sep 17 00:00:00 2001
From: Juan Ferrer Toribio <juan@verdnatura.es>
Date: Mon, 17 Mar 2025 17:50:56 +0100
Subject: [PATCH 12/34] refs #8776 PVE and Ceph monitoring

---
 roles/ceph/files/{nrpe.cfg => nrpe.d/95-ceph.cfg}     | 0
 roles/ceph/files/{nrpe => plugins}/check_ceph_mon     | 0
 roles/ceph/files/{nrpe => plugins}/check_ceph_osd     | 0
 roles/ceph/files/{nrpe => plugins}/check_chrony       | 0
 roles/ceph/files/{nrpe => plugins}/check_smartdisk.sh | 0
 roles/ceph/tasks/nrpe.yml                             | 6 +++---
 6 files changed, 3 insertions(+), 3 deletions(-)
 rename roles/ceph/files/{nrpe.cfg => nrpe.d/95-ceph.cfg} (100%)
 rename roles/ceph/files/{nrpe => plugins}/check_ceph_mon (100%)
 rename roles/ceph/files/{nrpe => plugins}/check_ceph_osd (100%)
 rename roles/ceph/files/{nrpe => plugins}/check_chrony (100%)
 rename roles/ceph/files/{nrpe => plugins}/check_smartdisk.sh (100%)

diff --git a/roles/ceph/files/nrpe.cfg b/roles/ceph/files/nrpe.d/95-ceph.cfg
similarity index 100%
rename from roles/ceph/files/nrpe.cfg
rename to roles/ceph/files/nrpe.d/95-ceph.cfg
diff --git a/roles/ceph/files/nrpe/check_ceph_mon b/roles/ceph/files/plugins/check_ceph_mon
similarity index 100%
rename from roles/ceph/files/nrpe/check_ceph_mon
rename to roles/ceph/files/plugins/check_ceph_mon
diff --git a/roles/ceph/files/nrpe/check_ceph_osd b/roles/ceph/files/plugins/check_ceph_osd
similarity index 100%
rename from roles/ceph/files/nrpe/check_ceph_osd
rename to roles/ceph/files/plugins/check_ceph_osd
diff --git a/roles/ceph/files/nrpe/check_chrony b/roles/ceph/files/plugins/check_chrony
similarity index 100%
rename from roles/ceph/files/nrpe/check_chrony
rename to roles/ceph/files/plugins/check_chrony
diff --git a/roles/ceph/files/nrpe/check_smartdisk.sh b/roles/ceph/files/plugins/check_smartdisk.sh
similarity index 100%
rename from roles/ceph/files/nrpe/check_smartdisk.sh
rename to roles/ceph/files/plugins/check_smartdisk.sh
diff --git a/roles/ceph/tasks/nrpe.yml b/roles/ceph/tasks/nrpe.yml
index c94df0a..2b87bbe 100644
--- a/roles/ceph/tasks/nrpe.yml
+++ b/roles/ceph/tasks/nrpe.yml
@@ -1,14 +1,14 @@
 - name: Set NRPE Ceph configuration
   copy:
-    src: nrpe.cfg
-    dest: /etc/nagios/nrpe.d/95-ceph.cfg
+    src: nrpe.d/95-ceph.cfg
+    dest: /etc/nagios/nrpe.d/
     owner: root
     group: root
     mode: u=rw,g=r,o=r
   notify: restart-nrpe
 - name: Copy Ceph NRPE plugins
   copy:
-    src: nrpe/
+    src: plugins/
     dest: /etc/nagios/plugins/
     owner: root
     group: root

From d7a1de2f5cc0f52b5c11fcc81181d60c3bacac21 Mon Sep 17 00:00:00 2001
From: Juan Ferrer Toribio <juan@verdnatura.es>
Date: Mon, 17 Mar 2025 17:51:25 +0100
Subject: [PATCH 13/34] refs #8776 PVE and Ceph monitoring

---
 roles/pve/tasks/nrpe.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/roles/pve/tasks/nrpe.yml b/roles/pve/tasks/nrpe.yml
index f242d49..4d70576 100644
--- a/roles/pve/tasks/nrpe.yml
+++ b/roles/pve/tasks/nrpe.yml
@@ -1,4 +1,4 @@
-- name: Install base packages
+- name: Install PVE NRPE base packages
   apt:
     name:
       - libxml-simple-perl

From 93586290dbc4cbfcf99f2b509f84e0fc2c1fa813 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es>
Date: Wed, 26 Mar 2025 13:15:41 +0100
Subject: [PATCH 14/34] dns: refs #8552 - Main approche - before final
 named.conf

---
 playbooks/ns.yml                        |  6 +++
 roles/ns/defaults/main.yml              | 28 ++++++++++
 roles/ns/files/delete.ns                | 14 +++++
 roles/ns/files/gen-key.sh               | 10 ++++
 roles/ns/files/isp1.ns                  | 16 ++++++
 roles/ns/files/isp2.ns                  | 16 ++++++
 roles/ns/files/switch-isp.sh            | 23 ++++++++
 roles/ns/files/sync-conf                | 27 ++++++++++
 roles/ns/handlers/main.yml              |  4 ++
 roles/ns/tasks/main.yml                 |  2 +
 roles/ns/tasks/ns.yml                   | 34 ++++++++++++
 roles/ns/templates/certbot.key          |  4 ++
 roles/ns/templates/dhcp.key             |  4 ++
 roles/ns/templates/lan.key              |  4 ++
 roles/ns/templates/named.conf.local.j2  | 20 +++++++
 roles/ns/templates/named.conf.master.j2 | 72 +++++++++++++++++++++++++
 roles/ns/templates/named.conf.slave.j2  | 68 +++++++++++++++++++++++
 roles/ns/templates/rndc.key             |  4 ++
 roles/ns/templates/wan.key              |  4 ++
 19 files changed, 360 insertions(+)
 create mode 100644 playbooks/ns.yml
 create mode 100644 roles/ns/defaults/main.yml
 create mode 100644 roles/ns/files/delete.ns
 create mode 100644 roles/ns/files/gen-key.sh
 create mode 100644 roles/ns/files/isp1.ns
 create mode 100644 roles/ns/files/isp2.ns
 create mode 100644 roles/ns/files/switch-isp.sh
 create mode 100644 roles/ns/files/sync-conf
 create mode 100644 roles/ns/handlers/main.yml
 create mode 100644 roles/ns/tasks/main.yml
 create mode 100644 roles/ns/tasks/ns.yml
 create mode 100644 roles/ns/templates/certbot.key
 create mode 100644 roles/ns/templates/dhcp.key
 create mode 100644 roles/ns/templates/lan.key
 create mode 100644 roles/ns/templates/named.conf.local.j2
 create mode 100644 roles/ns/templates/named.conf.master.j2
 create mode 100644 roles/ns/templates/named.conf.slave.j2
 create mode 100644 roles/ns/templates/rndc.key
 create mode 100644 roles/ns/templates/wan.key

diff --git a/playbooks/ns.yml b/playbooks/ns.yml
new file mode 100644
index 0000000..e29c441
--- /dev/null
+++ b/playbooks/ns.yml
@@ -0,0 +1,6 @@
+- name: Configure bind9 name server
+  hosts: all
+  tasks:
+  - name: Configure services to install in the server
+    import_role:
+      name: ns
\ No newline at end of file
diff --git a/roles/ns/defaults/main.yml b/roles/ns/defaults/main.yml
new file mode 100644
index 0000000..f0be459
--- /dev/null
+++ b/roles/ns/defaults/main.yml
@@ -0,0 +1,28 @@
+bind_packages:
+  - bind9
+  - bind9-dnsutils
+  - bind9-host
+  - bind9-libs
+  - bind9-utils
+  - dnsutils
+  - python3-pycurl
+bind_config_templates:
+  - { src: 'named.conf.master.j2', dest: '/etc/bind/named.conf.master', mode: 'u=rw,g=r,o=r' }
+  - { src: 'named.conf.local.j2',  dest: '/etc/bind/named.conf.local',  mode: 'u=rw,g=r,o=r' }
+  - { src: 'named.conf.slave.j2', dest: '/etc/bind/named.conf.slave', mode: 'u=rw,g=r,o=r' }
+  - { src: 'certbot.key', dest: '/etc/bind/keys/certbot.key', mode: 'u=rw,g=r,o=' }
+  - { src: 'lan.key', dest: '/etc/bind/keys/lan.key', mode: 'u=rw,g=r,o=' }
+  - { src: 'wan.key', dest: '/etc/bind/keys/wan.key', mode: 'u=rw,g=r,o=' }
+  - { src: 'rndc.key', dest: '/etc/bind/rndc.key', mode: 'u=rw,g=r,o=' }
+  - { src: 'dhcp.key', dest: '/etc/bind/keys/dhcp.key', mode: 'u=rw,g=r,o=' }
+directory:
+  - { path: '/root/scripts', owner: 'root', group: 'root', mode: 'u=rwx,g=rx,o=rx' }
+  - { path: '/etc/bind/keys', owner: 'root', group: 'bind', mode: 'u=rwx,g=rs,o=rx' }
+  - { path: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rwx,g=rs,o=rx' }
+required_files:
+  - { src: 'delete.ns', dest: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rw,g=rw,o=r' }
+  - { src: 'isp1.ns', dest: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rw,g=rw,o=r' }
+  - { src: 'isp2.ns', dest: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rw,g=rw,o=r' }
+  - { src: 'switch-isp.sh', dest: '/root/scripts', owner: 'root', group: 'root', mode: 'u=rwx,g=rx,o=rx' }
+  - { src: 'sync-conf', dest: '/root/scripts', owner: 'root', group: 'root', mode: 'u=rwx,g=rx,o=rx' }
+  - { src: 'gen-key.sh', dest: '/root/scripts', owner: 'root', group: 'bind', mode: 'u=rwx,g=rx,o=rx' }
diff --git a/roles/ns/files/delete.ns b/roles/ns/files/delete.ns
new file mode 100644
index 0000000..cbb97ad
--- /dev/null
+++ b/roles/ns/files/delete.ns
@@ -0,0 +1,14 @@
+update delete verdnatura.es              A
+update delete kube-proxy.verdnatura.es   A
+update delete smtp.verdnatura.es         A
+update delete imap.verdnatura.es         A
+update delete autodiscover.verdnatura.es A
+update delete time1.verdnatura.es        A
+update delete time2.verdnatura.es        A
+update delete dc-ip01.verdnatura.es      A
+update delete dc-ip02.verdnatura.es      A
+update delete dc-ip03.verdnatura.es      A
+update delete dc-ip04.verdnatura.es      A
+update delete mailgw1.verdnatura.es      A
+update delete mailgw2.verdnatura.es      A
+send
diff --git a/roles/ns/files/gen-key.sh b/roles/ns/files/gen-key.sh
new file mode 100644
index 0000000..4c062a2
--- /dev/null
+++ b/roles/ns/files/gen-key.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+KEYNAME=$1
+
+if [ -z "$KEYNAME" ]; then
+    echo "Usage: $0 <key_name>"
+    exit 1
+fi
+
+tsig-keygen -a hmac-sha512 "$KEYNAME"
diff --git a/roles/ns/files/isp1.ns b/roles/ns/files/isp1.ns
new file mode 100644
index 0000000..e9ed9b6
--- /dev/null
+++ b/roles/ns/files/isp1.ns
@@ -0,0 +1,16 @@
+update add verdnatura.es               3600   A  89.6.245.230
+update add verdnatura.es               3600   A  89.6.245.231
+update add kube-proxy.verdnatura.es    3600   A  89.6.245.230
+update add kube-proxy.verdnatura.es    3600   A  89.6.245.231
+update add smtp.verdnatura.es          3600   A  89.6.245.230
+update add imap.verdnatura.es          3600   A  89.6.245.230
+update add autodiscover.verdnatura.es  3600   A  89.6.245.230
+update add time1.verdnatura.es         3600   A  89.6.245.230
+update add time2.verdnatura.es         3600   A  89.6.245.230
+update add dc-ip01.verdnatura.es       3600   A  89.6.245.228
+update add dc-ip02.verdnatura.es       3600   A  89.6.245.229
+update add dc-ip03.verdnatura.es       3600   A  89.6.245.230
+update add dc-ip04.verdnatura.es       3600   A  89.6.245.231
+update add mailgw1.verdnatura.es       43200  A  89.6.245.232
+update add mailgw2.verdnatura.es       43200  A  89.6.245.233
+send
diff --git a/roles/ns/files/isp2.ns b/roles/ns/files/isp2.ns
new file mode 100644
index 0000000..f5bd027
--- /dev/null
+++ b/roles/ns/files/isp2.ns
@@ -0,0 +1,16 @@
+update add verdnatura.es               3600   A  195.77.191.180
+update add verdnatura.es               3600   A  195.77.191.181
+update add kube-proxy.verdnatura.es    3600   A  195.77.191.180
+update add kube-proxy.verdnatura.es    3600   A  195.77.191.181
+update add smtp.verdnatura.es          3600   A  195.77.191.180
+update add imap.verdnatura.es          3600   A  195.77.191.180
+update add autodiscover.verdnatura.es  3600   A  195.77.191.180
+update add time1.verdnatura.es         3600   A  195.77.191.180
+update add time2.verdnatura.es         3600   A  195.77.191.180
+update add dc-ip01.verdnatura.es       3600   A  195.77.191.178
+update add dc-ip02.verdnatura.es       3600   A  195.77.191.179
+update add dc-ip03.verdnatura.es       3600   A  195.77.191.180
+update add dc-ip04.verdnatura.es       3600   A  195.77.191.181
+update add mailgw1.verdnatura.es       43200  A  195.77.191.180
+update add mailgw2.verdnatura.es       43200  A  195.77.191.181
+send
diff --git a/roles/ns/files/switch-isp.sh b/roles/ns/files/switch-isp.sh
new file mode 100644
index 0000000..5444192
--- /dev/null
+++ b/roles/ns/files/switch-isp.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+ISP=$1
+
+if [ -z "$ISP" ]; then
+    echo "Usage: $0 <isp_name>"
+    exit 1
+fi
+
+KEY_FILE="/etc/bind/keys/wan.key"
+NS_DIR="/root/scripts/switch-isp"
+ISP_FILE="$NS_DIR/$ISP.ns"
+
+if [ ! -f "$ISP_FILE" ]; then
+    echo "ISP file for nsupdate not found: $ISP_FILE"
+    exit 2
+fi
+
+echo "Deleting ISP dependent DNS records."
+nsupdate -k "$KEY_FILE" "$NS_DIR/delete.ns"
+
+echo "Registering $ISP DNS records."
+nsupdate -k "$KEY_FILE" "$ISP_FILE"
diff --git a/roles/ns/files/sync-conf b/roles/ns/files/sync-conf
new file mode 100644
index 0000000..a3b947b
--- /dev/null
+++ b/roles/ns/files/sync-conf
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+if [ "$(hostname)" = "ns1" ]; then
+	partner=root@ns2.servers.dc.verdnatura.es
+else
+	partner=root@ns1.servers.dc.verdnatura.es
+fi
+
+confDir=/etc/bind
+
+echo "Restarting service."
+service bind9 restart
+
+if [ $? -eq "0" ]; then
+	echo "Synchronizing partner configuration."
+
+	scp "$confDir/named.conf.local" $partner:$confDir
+	scp "$confDir/named.conf.master" $partner:$confDir
+	scp "$confDir/named.conf.slave" $partner:$confDir
+
+	ssh "$partner" rm -rf "$confDir/keys"
+	ssh "$partner" mkdir "$confDir/keys"
+	scp -r "$confDir/keys/"* $partner:"$confDir/keys"
+
+	echo "Restarting partner service."
+	ssh $partner service bind9 restart
+fi
diff --git a/roles/ns/handlers/main.yml b/roles/ns/handlers/main.yml
new file mode 100644
index 0000000..840d041
--- /dev/null
+++ b/roles/ns/handlers/main.yml
@@ -0,0 +1,4 @@
+- name: restart-dns
+  systemd:
+    name: bind9
+    state: restarted
\ No newline at end of file
diff --git a/roles/ns/tasks/main.yml b/roles/ns/tasks/main.yml
new file mode 100644
index 0000000..0948ab4
--- /dev/null
+++ b/roles/ns/tasks/main.yml
@@ -0,0 +1,2 @@
+- import_tasks: ns.yml
+  tags: ns
diff --git a/roles/ns/tasks/ns.yml b/roles/ns/tasks/ns.yml
new file mode 100644
index 0000000..7943efa
--- /dev/null
+++ b/roles/ns/tasks/ns.yml
@@ -0,0 +1,34 @@
+- name: Update apt cache
+  apt:
+   update_cache: yes
+- name: Install bind package requirements
+  apt:
+    name: "{{ bind_packages }}"
+    state: present
+    install_recommends: no
+- name: Create directory
+  file:
+    path: "{{ item.path }}"
+    state: directory
+    owner: "{{ item.group }}"
+    group: "{{ item.group }}"
+    mode: "{{ item.mode }}"
+  loop: "{{ directory }}"
+- name: Copy required files and scripts
+  copy:
+    src: "{{ item.src }}"
+    dest: "{{ item.dest }}"
+    owner: "{{ item.group }}"
+    group: "{{ item.group }}"
+    mode: "{{ item.mode }}"
+  loop: "{{ required_files }}"
+- name: Deploy BIND config templates
+  template:
+    src: "{{ item.src }}"
+    dest: "{{ item.dest }}"
+    owner: root
+    group: bind
+    mode: "{{ item.mode }}"
+  loop: "{{ bind_config_templates }}"
+  notify: restart-dns
+  
diff --git a/roles/ns/templates/certbot.key b/roles/ns/templates/certbot.key
new file mode 100644
index 0000000..6e9f1b9
--- /dev/null
+++ b/roles/ns/templates/certbot.key
@@ -0,0 +1,4 @@
+key "certbot" {
+	algorithm hmac-sha512;
+	secret "{{ lookup(passbolt, 'certbot', folder_parent_id=passbolt_folder).password }}";
+};
diff --git a/roles/ns/templates/dhcp.key b/roles/ns/templates/dhcp.key
new file mode 100644
index 0000000..a732d1e
--- /dev/null
+++ b/roles/ns/templates/dhcp.key
@@ -0,0 +1,4 @@
+key "dhcp" {
+	algorithm hmac-sha512;
+	secret "{{ lookup(passbolt, 'dhcpkey', folder_parent_id=passbolt_folder).password }}";
+};
diff --git a/roles/ns/templates/lan.key b/roles/ns/templates/lan.key
new file mode 100644
index 0000000..c99ad33
--- /dev/null
+++ b/roles/ns/templates/lan.key
@@ -0,0 +1,4 @@
+key "lan" {
+	algorithm hmac-sha512;
+	secret "{{ lookup(passbolt, 'lankey', folder_parent_id=passbolt_folder).password }}";
+};
diff --git a/roles/ns/templates/named.conf.local.j2 b/roles/ns/templates/named.conf.local.j2
new file mode 100644
index 0000000..45ac42f
--- /dev/null
+++ b/roles/ns/templates/named.conf.local.j2
@@ -0,0 +1,20 @@
+{% for path in bind_key_includes %}
+include "{{ path }}";
+{% endfor %}
+
+{% for server in bind_bogus_servers %}
+server {{ server }} { bogus yes; };
+{% endfor %}
+
+{% for acl_name, networks in bind_acls.items() %}
+acl {{ acl_name }} {
+  {% for net in networks %}
+  {{ net }};
+  {% endfor %}
+};
+{% endfor %}
+
+controls {
+	inet {{ bind_controls["inet"] }} allow { {{ bind_controls["allow"] | join('; ') }}; } keys { {{ bind_controls["keys"] | map('regex_replace', '^(.*)$', '"\\1"') | join('; ') }}; };
+};
+
diff --git a/roles/ns/templates/named.conf.master.j2 b/roles/ns/templates/named.conf.master.j2
new file mode 100644
index 0000000..814c5a8
--- /dev/null
+++ b/roles/ns/templates/named.conf.master.j2
@@ -0,0 +1,72 @@
+options {
+    directory "{{ bind_cache_dir }}";
+    max-cache-size {{ bind_max_cache_size }};
+    auth-nxdomain no;
+    listen-on-v6 { none; };
+    version "{{ bind_version }}";
+    allow-update { none; };
+    blackhole { rfc5735; };
+
+    allow-transfer {
+    {% for ip in bind_allow_transfer %}
+        {{ ip }};
+    {% endfor %}
+    };
+};
+
+view "lan" {
+    match-clients {
+    {% for item in bind_match_clients_lan %}
+        {{ item }};
+    {% endfor %}
+    };
+
+    recursion yes;
+    allow-recursion { any; };
+    empty-zones-enable yes;
+    notify {{ bind_notify_lan | default('yes') }};
+
+    include "/etc/bind/named.conf.default-zones";
+
+    {% for zone in bind_zones.lan %}
+    zone "{{ zone.name }}" {
+        type master;
+        forwarders {};
+        allow-update { key {{ zone.key }}; };
+        file "{{ zone.file }}";
+    };
+    {% endfor %}
+};
+
+view "wan" {
+    match-clients { any; };
+
+    recursion no;
+    allow-query-cache { none; };
+    empty-zones-enable no;
+
+    notify {{ bind_notify_wan }};
+
+    also-notify {
+    {% for entry in bind_also_notify %}
+        {{ entry.ip }} key {{ entry.key }};
+    {% endfor %}
+    };
+
+    {% for zone in bind_zones.wan %}
+    {% if zone.in_view is defined %}
+    {% for z in zone.in_view %}
+    zone "{{ z }}" {
+        in-view "lan";
+    };
+    {% endfor %}
+    {% else %}
+    zone "{{ zone.name }}" {
+        type master;
+        forwarders {};
+        allow-update { key {{ zone.key }}; };
+        file "{{ zone.file }}";
+    };
+    {% endif %}
+    {% endfor %}
+};
diff --git a/roles/ns/templates/named.conf.slave.j2 b/roles/ns/templates/named.conf.slave.j2
new file mode 100644
index 0000000..b4078b1
--- /dev/null
+++ b/roles/ns/templates/named.conf.slave.j2
@@ -0,0 +1,68 @@
+options {
+    directory "{{ bind_cache_dir }}";
+
+    max-cache-size {{ bind_max_cache_size }};
+    auth-nxdomain no;
+    listen-on-v6 { none; };
+    version "{{ bind_version }}";
+    allow-update { none; };
+    blackhole { rfc5735; };
+
+    notify {{ bind_slave_notify }};
+    allow-transfer { none; };
+    masterfile-format {{ bind_masterfile_format }};
+};
+
+masters master-ips {
+{% for ip in bind_slave_masters %}
+    {{ ip }};
+{% endfor %}
+};
+
+view "lan" {
+    match-clients {
+    {% for item in bind_match_clients_lan %}
+        {{ item }};
+    {% endfor %}
+    };
+
+    recursion yes;
+    allow-recursion { any; };
+    empty-zones-enable yes;
+
+    include "/etc/bind/named.conf.default-zones";
+
+    {% for zone in bind_zones.lan %}
+    zone "{{ zone.name }}" {
+        type slave;
+        masters { master-ips; };
+        forwarders {};
+        file "{{ zone.file }}";
+    };
+    {% endfor %}
+};
+
+view "wan" {
+    match-clients { any; };
+
+    recursion no;
+    allow-query-cache { none; };
+    empty-zones-enable no;
+
+    {% for zone in bind_zones.wan %}
+    {% if zone.in_view is defined %}
+    {% for z in zone.in_view %}
+    zone "{{ z }}" {
+        in-view "lan";
+    };
+    {% endfor %}
+    {% else %}
+    zone "{{ zone.name }}" {
+        type slave;
+        masters { {{ bind_slave_masters | join('; ') }} key {{ zone.key }}; };
+        forwarders {};
+        file "{{ zone.file }}";
+    };
+    {% endif %}
+    {% endfor %}
+};
diff --git a/roles/ns/templates/rndc.key b/roles/ns/templates/rndc.key
new file mode 100644
index 0000000..40d9600
--- /dev/null
+++ b/roles/ns/templates/rndc.key
@@ -0,0 +1,4 @@
+key "rndc-key" {
+	algorithm hmac-md5;
+	secret "{{ lookup(passbolt, 'rndc-key', folder_parent_id=passbolt_folder).password }}";
+};
diff --git a/roles/ns/templates/wan.key b/roles/ns/templates/wan.key
new file mode 100644
index 0000000..daa655c
--- /dev/null
+++ b/roles/ns/templates/wan.key
@@ -0,0 +1,4 @@
+key "wan-key" {
+	algorithm hmac-md5;
+	secret "{{ lookup(passbolt, 'wan-key', folder_parent_id=passbolt_folder).password }}";
+};

From 24d8864cf65d16f4fac3ef25883a256972fa687a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es>
Date: Wed, 26 Mar 2025 14:49:26 +0100
Subject: [PATCH 15/34] dns: refs #8552 - Main approche - final touch

---
 roles/ns/defaults/main.yml              |  5 ++--
 roles/ns/templates/named.conf.j2        | 16 ++++++++++
 roles/ns/templates/named.conf.local.j2  | 40 +++++++++++++++----------
 roles/ns/templates/named.conf.master.j2 | 24 +++++++++------
 roles/ns/templates/named.conf.slave.j2  | 25 +++++++++-------
 5 files changed, 74 insertions(+), 36 deletions(-)
 create mode 100644 roles/ns/templates/named.conf.j2

diff --git a/roles/ns/defaults/main.yml b/roles/ns/defaults/main.yml
index f0be459..528f6c4 100644
--- a/roles/ns/defaults/main.yml
+++ b/roles/ns/defaults/main.yml
@@ -7,6 +7,7 @@ bind_packages:
   - dnsutils
   - python3-pycurl
 bind_config_templates:
+  - { src: 'named.conf.j2', dest: '/etc/bind/named.conf', mode: 'u=rw,g=r,o=r' }
   - { src: 'named.conf.master.j2', dest: '/etc/bind/named.conf.master', mode: 'u=rw,g=r,o=r' }
   - { src: 'named.conf.local.j2',  dest: '/etc/bind/named.conf.local',  mode: 'u=rw,g=r,o=r' }
   - { src: 'named.conf.slave.j2', dest: '/etc/bind/named.conf.slave', mode: 'u=rw,g=r,o=r' }
@@ -17,8 +18,8 @@ bind_config_templates:
   - { src: 'dhcp.key', dest: '/etc/bind/keys/dhcp.key', mode: 'u=rw,g=r,o=' }
 directory:
   - { path: '/root/scripts', owner: 'root', group: 'root', mode: 'u=rwx,g=rx,o=rx' }
-  - { path: '/etc/bind/keys', owner: 'root', group: 'bind', mode: 'u=rwx,g=rs,o=rx' }
-  - { path: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rwx,g=rs,o=rx' }
+  - { path: '/etc/bind/keys', owner: 'root', group: 'bind', mode: 'u=rwx,g=rxs,o=rx' }
+  - { path: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rwx,g=rxs,o=rx' }
 required_files:
   - { src: 'delete.ns', dest: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rw,g=rw,o=r' }
   - { src: 'isp1.ns', dest: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rw,g=rw,o=r' }
diff --git a/roles/ns/templates/named.conf.j2 b/roles/ns/templates/named.conf.j2
new file mode 100644
index 0000000..32e13f7
--- /dev/null
+++ b/roles/ns/templates/named.conf.j2
@@ -0,0 +1,16 @@
+// This is the primary configuration file for the BIND DNS server named.
+//
+// Please read /usr/share/doc/bind9/README.Debian.gz for information on the 
+// structure of BIND configuration files in Debian, *BEFORE* you customize 
+// this configuration file.
+//
+// If you are just adding zones, please do that in /etc/bind/named.conf.local
+
+#include "/etc/bind/named.conf.options";
+include "/etc/bind/named.conf.local";
+{% if bind_is_master %}
+include "/etc/bind/named.conf.master";
+{% else %}
+include "/etc/bind/named.conf.slave";
+{% endif %}
+#include "/etc/bind/named.conf.default-zones";
diff --git a/roles/ns/templates/named.conf.local.j2 b/roles/ns/templates/named.conf.local.j2
index 45ac42f..09a8935 100644
--- a/roles/ns/templates/named.conf.local.j2
+++ b/roles/ns/templates/named.conf.local.j2
@@ -1,20 +1,30 @@
-{% for path in bind_key_includes %}
-include "{{ path }}";
-{% endfor %}
+include "/etc/bind/rndc.key";
+include "/etc/bind/keys/wan.key";
+include "/etc/bind/keys/lan.key";
+include "/etc/bind/keys/certbot.key";
+include "/etc/bind/keys/dhcp.key";
 
-{% for server in bind_bogus_servers %}
-server {{ server }} { bogus yes; };
-{% endfor %}
+server fe80::/16 { bogus yes; };
 
-{% for acl_name, networks in bind_acls.items() %}
-acl {{ acl_name }} {
-  {% for net in networks %}
-  {{ net }};
-  {% endfor %}
-};
-{% endfor %}
+acl lan {
+    10.0.0.0/8;
+    172.16.0.0/12;
+    192.168.0.0/16;
+  };
+  
+acl rfc5735 {
+    0.0.0.0/8;
+    169.254.0.0/16;
+    192.0.0.0/24;
+    192.0.2.0/24;
+    192.88.99.0/24;
+    198.18.0.0/15;
+    198.51.100.0/24;
+    203.0.113.0/24;
+    224.0.0.0/4;
+    240.0.0.0/4;
+  };
 
 controls {
-	inet {{ bind_controls["inet"] }} allow { {{ bind_controls["allow"] | join('; ') }}; } keys { {{ bind_controls["keys"] | map('regex_replace', '^(.*)$', '"\\1"') | join('; ') }}; };
+	inet * allow { localhost; lan; } keys { "rndc-key"; };
 };
-
diff --git a/roles/ns/templates/named.conf.master.j2 b/roles/ns/templates/named.conf.master.j2
index 814c5a8..dc63834 100644
--- a/roles/ns/templates/named.conf.master.j2
+++ b/roles/ns/templates/named.conf.master.j2
@@ -1,9 +1,9 @@
 options {
-    directory "{{ bind_cache_dir }}";
-    max-cache-size {{ bind_max_cache_size }};
+    directory "/var/cache/bind";
+    max-cache-size 500m;
     auth-nxdomain no;
     listen-on-v6 { none; };
-    version "{{ bind_version }}";
+    version "DNS";
     allow-update { none; };
     blackhole { rfc5735; };
 
@@ -15,16 +15,22 @@ options {
 };
 
 view "lan" {
-    match-clients {
-    {% for item in bind_match_clients_lan %}
+        match-clients {
+        {% for item in key_match_clients_lan_master if item.startswith("!key") %}
+          {{ item }};
+          {% endfor %}
+{% for item in acl_match_clients %}
         {{ item }};
-    {% endfor %}
-    };
+          {% endfor %}
+{% for item in key_match_clients_lan_master if not item.startswith("!key") %}
+        {{ item }};
+          {% endfor %}
+};
 
     recursion yes;
     allow-recursion { any; };
     empty-zones-enable yes;
-    notify {{ bind_notify_lan | default('yes') }};
+    notify yes;
 
     include "/etc/bind/named.conf.default-zones";
 
@@ -45,7 +51,7 @@ view "wan" {
     allow-query-cache { none; };
     empty-zones-enable no;
 
-    notify {{ bind_notify_wan }};
+    notify explicit;
 
     also-notify {
     {% for entry in bind_also_notify %}
diff --git a/roles/ns/templates/named.conf.slave.j2 b/roles/ns/templates/named.conf.slave.j2
index b4078b1..c1142a1 100644
--- a/roles/ns/templates/named.conf.slave.j2
+++ b/roles/ns/templates/named.conf.slave.j2
@@ -1,16 +1,15 @@
 options {
-    directory "{{ bind_cache_dir }}";
-
-    max-cache-size {{ bind_max_cache_size }};
+    directory "/var/cache/bind";
+    max-cache-size 500m;
     auth-nxdomain no;
     listen-on-v6 { none; };
-    version "{{ bind_version }}";
+    version "DNS";
     allow-update { none; };
     blackhole { rfc5735; };
 
-    notify {{ bind_slave_notify }};
+    notify no;
     allow-transfer { none; };
-    masterfile-format {{ bind_masterfile_format }};
+    masterfile-format text;
 };
 
 masters master-ips {
@@ -20,11 +19,17 @@ masters master-ips {
 };
 
 view "lan" {
-    match-clients {
-    {% for item in bind_match_clients_lan %}
+        match-clients {
+        {% for item in key_match_clients_lan_master if item.startswith("!key") %}
+          {{ item }};
+          {% endfor %}
+{% for item in acl_match_clients %}
         {{ item }};
-    {% endfor %}
-    };
+          {% endfor %}
+{% for item in key_match_clients_lan_slave if not item.startswith("!key") %}
+        {{ item }};
+          {% endfor %}
+};
 
     recursion yes;
     allow-recursion { any; };

From babf43cfba656526271c105aa08a60151b83a7fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es>
Date: Thu, 27 Mar 2025 10:43:50 +0100
Subject: [PATCH 16/34] dns: refs #8552 - Juan Request
 https://gitea.verdnatura.es/verdnatura/vn-ansible/pulls/79

---
 roles/ns/defaults/main.yml |  4 ++--
 roles/ns/files/isp1.ns     | 16 ----------------
 roles/ns/files/isp2.ns     | 16 ----------------
 roles/ns/templates/isp1.ns |  4 ++++
 roles/ns/templates/isp2.ns |  4 ++++
 5 files changed, 10 insertions(+), 34 deletions(-)
 delete mode 100644 roles/ns/files/isp1.ns
 delete mode 100644 roles/ns/files/isp2.ns
 create mode 100644 roles/ns/templates/isp1.ns
 create mode 100644 roles/ns/templates/isp2.ns

diff --git a/roles/ns/defaults/main.yml b/roles/ns/defaults/main.yml
index 528f6c4..daeccf4 100644
--- a/roles/ns/defaults/main.yml
+++ b/roles/ns/defaults/main.yml
@@ -16,14 +16,14 @@ bind_config_templates:
   - { src: 'wan.key', dest: '/etc/bind/keys/wan.key', mode: 'u=rw,g=r,o=' }
   - { src: 'rndc.key', dest: '/etc/bind/rndc.key', mode: 'u=rw,g=r,o=' }
   - { src: 'dhcp.key', dest: '/etc/bind/keys/dhcp.key', mode: 'u=rw,g=r,o=' }
+  - { src: 'isp1.ns', dest: '/root/scripts/switch-isp', mode: 'u=rw,g=rw,o=r' }
+  - { src: 'isp2.ns', dest: '/root/scripts/switch-isp', mode: 'u=rw,g=rw,o=r' }
 directory:
   - { path: '/root/scripts', owner: 'root', group: 'root', mode: 'u=rwx,g=rx,o=rx' }
   - { path: '/etc/bind/keys', owner: 'root', group: 'bind', mode: 'u=rwx,g=rxs,o=rx' }
   - { path: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rwx,g=rxs,o=rx' }
 required_files:
   - { src: 'delete.ns', dest: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rw,g=rw,o=r' }
-  - { src: 'isp1.ns', dest: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rw,g=rw,o=r' }
-  - { src: 'isp2.ns', dest: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rw,g=rw,o=r' }
   - { src: 'switch-isp.sh', dest: '/root/scripts', owner: 'root', group: 'root', mode: 'u=rwx,g=rx,o=rx' }
   - { src: 'sync-conf', dest: '/root/scripts', owner: 'root', group: 'root', mode: 'u=rwx,g=rx,o=rx' }
   - { src: 'gen-key.sh', dest: '/root/scripts', owner: 'root', group: 'bind', mode: 'u=rwx,g=rx,o=rx' }
diff --git a/roles/ns/files/isp1.ns b/roles/ns/files/isp1.ns
deleted file mode 100644
index e9ed9b6..0000000
--- a/roles/ns/files/isp1.ns
+++ /dev/null
@@ -1,16 +0,0 @@
-update add verdnatura.es               3600   A  89.6.245.230
-update add verdnatura.es               3600   A  89.6.245.231
-update add kube-proxy.verdnatura.es    3600   A  89.6.245.230
-update add kube-proxy.verdnatura.es    3600   A  89.6.245.231
-update add smtp.verdnatura.es          3600   A  89.6.245.230
-update add imap.verdnatura.es          3600   A  89.6.245.230
-update add autodiscover.verdnatura.es  3600   A  89.6.245.230
-update add time1.verdnatura.es         3600   A  89.6.245.230
-update add time2.verdnatura.es         3600   A  89.6.245.230
-update add dc-ip01.verdnatura.es       3600   A  89.6.245.228
-update add dc-ip02.verdnatura.es       3600   A  89.6.245.229
-update add dc-ip03.verdnatura.es       3600   A  89.6.245.230
-update add dc-ip04.verdnatura.es       3600   A  89.6.245.231
-update add mailgw1.verdnatura.es       43200  A  89.6.245.232
-update add mailgw2.verdnatura.es       43200  A  89.6.245.233
-send
diff --git a/roles/ns/files/isp2.ns b/roles/ns/files/isp2.ns
deleted file mode 100644
index f5bd027..0000000
--- a/roles/ns/files/isp2.ns
+++ /dev/null
@@ -1,16 +0,0 @@
-update add verdnatura.es               3600   A  195.77.191.180
-update add verdnatura.es               3600   A  195.77.191.181
-update add kube-proxy.verdnatura.es    3600   A  195.77.191.180
-update add kube-proxy.verdnatura.es    3600   A  195.77.191.181
-update add smtp.verdnatura.es          3600   A  195.77.191.180
-update add imap.verdnatura.es          3600   A  195.77.191.180
-update add autodiscover.verdnatura.es  3600   A  195.77.191.180
-update add time1.verdnatura.es         3600   A  195.77.191.180
-update add time2.verdnatura.es         3600   A  195.77.191.180
-update add dc-ip01.verdnatura.es       3600   A  195.77.191.178
-update add dc-ip02.verdnatura.es       3600   A  195.77.191.179
-update add dc-ip03.verdnatura.es       3600   A  195.77.191.180
-update add dc-ip04.verdnatura.es       3600   A  195.77.191.181
-update add mailgw1.verdnatura.es       43200  A  195.77.191.180
-update add mailgw2.verdnatura.es       43200  A  195.77.191.181
-send
diff --git a/roles/ns/templates/isp1.ns b/roles/ns/templates/isp1.ns
new file mode 100644
index 0000000..57eba65
--- /dev/null
+++ b/roles/ns/templates/isp1.ns
@@ -0,0 +1,4 @@
+{% for record in dns_records_main %}
+update add {{ record.name.ljust(30) }} {{ record.ttl }}   A  {{ record.ip }}
+{% endfor %}
+send
diff --git a/roles/ns/templates/isp2.ns b/roles/ns/templates/isp2.ns
new file mode 100644
index 0000000..e89b38e
--- /dev/null
+++ b/roles/ns/templates/isp2.ns
@@ -0,0 +1,4 @@
+{% for record in dns_records_secondary %}
+update add {{ record.name.ljust(30) }} {{ record.ttl }}   A  {{ record.ip }}
+{% endfor %}
+send

From 651ee7edf620b7a7eba914da4884b1a05a2ce1da Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es>
Date: Fri, 28 Mar 2025 14:12:42 +0100
Subject: [PATCH 17/34] dns: refs #8552 - disable ipv6 and move delete ns file

---
 roles/ns/defaults/main.yml              |  2 +-
 roles/ns/files/delete.ns                | 14 --------------
 roles/ns/tasks/ns.yml                   |  7 +++++++
 roles/ns/templates/delete.ns            |  4 ++++
 roles/ns/templates/named.conf.master.j2 |  6 ++++++
 roles/ns/templates/named.conf.slave.j2  | 11 ++++++++---
 6 files changed, 26 insertions(+), 18 deletions(-)
 delete mode 100644 roles/ns/files/delete.ns
 create mode 100644 roles/ns/templates/delete.ns

diff --git a/roles/ns/defaults/main.yml b/roles/ns/defaults/main.yml
index daeccf4..4b1213f 100644
--- a/roles/ns/defaults/main.yml
+++ b/roles/ns/defaults/main.yml
@@ -18,12 +18,12 @@ bind_config_templates:
   - { src: 'dhcp.key', dest: '/etc/bind/keys/dhcp.key', mode: 'u=rw,g=r,o=' }
   - { src: 'isp1.ns', dest: '/root/scripts/switch-isp', mode: 'u=rw,g=rw,o=r' }
   - { src: 'isp2.ns', dest: '/root/scripts/switch-isp', mode: 'u=rw,g=rw,o=r' }
+  - { src: 'delete.ns', dest: '/root/scripts/switch-isp', mode: 'u=rw,g=rw,o=r' }
 directory:
   - { path: '/root/scripts', owner: 'root', group: 'root', mode: 'u=rwx,g=rx,o=rx' }
   - { path: '/etc/bind/keys', owner: 'root', group: 'bind', mode: 'u=rwx,g=rxs,o=rx' }
   - { path: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rwx,g=rxs,o=rx' }
 required_files:
-  - { src: 'delete.ns', dest: '/root/scripts/switch-isp', owner: 'root', group: 'bind', mode: 'u=rw,g=rw,o=r' }
   - { src: 'switch-isp.sh', dest: '/root/scripts', owner: 'root', group: 'root', mode: 'u=rwx,g=rx,o=rx' }
   - { src: 'sync-conf', dest: '/root/scripts', owner: 'root', group: 'root', mode: 'u=rwx,g=rx,o=rx' }
   - { src: 'gen-key.sh', dest: '/root/scripts', owner: 'root', group: 'bind', mode: 'u=rwx,g=rx,o=rx' }
diff --git a/roles/ns/files/delete.ns b/roles/ns/files/delete.ns
deleted file mode 100644
index cbb97ad..0000000
--- a/roles/ns/files/delete.ns
+++ /dev/null
@@ -1,14 +0,0 @@
-update delete verdnatura.es              A
-update delete kube-proxy.verdnatura.es   A
-update delete smtp.verdnatura.es         A
-update delete imap.verdnatura.es         A
-update delete autodiscover.verdnatura.es A
-update delete time1.verdnatura.es        A
-update delete time2.verdnatura.es        A
-update delete dc-ip01.verdnatura.es      A
-update delete dc-ip02.verdnatura.es      A
-update delete dc-ip03.verdnatura.es      A
-update delete dc-ip04.verdnatura.es      A
-update delete mailgw1.verdnatura.es      A
-update delete mailgw2.verdnatura.es      A
-send
diff --git a/roles/ns/tasks/ns.yml b/roles/ns/tasks/ns.yml
index 7943efa..beb5d3a 100644
--- a/roles/ns/tasks/ns.yml
+++ b/roles/ns/tasks/ns.yml
@@ -6,6 +6,13 @@
     name: "{{ bind_packages }}"
     state: present
     install_recommends: no
+- name: Ensure BIND9 starts with IPv4 only (-4)
+  lineinfile:
+    path: /etc/default/named
+    regexp: '^OPTIONS='
+    line: 'OPTIONS="-u bind -4"'
+    backrefs: yes
+  notify: restart-dns
 - name: Create directory
   file:
     path: "{{ item.path }}"
diff --git a/roles/ns/templates/delete.ns b/roles/ns/templates/delete.ns
new file mode 100644
index 0000000..899f5cc
--- /dev/null
+++ b/roles/ns/templates/delete.ns
@@ -0,0 +1,4 @@
+{% for record in dns_records_delete %}
+update delete {{ record.name.ljust(30) }} A  
+{% endfor %}
+send
diff --git a/roles/ns/templates/named.conf.master.j2 b/roles/ns/templates/named.conf.master.j2
index dc63834..e923cc5 100644
--- a/roles/ns/templates/named.conf.master.j2
+++ b/roles/ns/templates/named.conf.master.j2
@@ -27,6 +27,12 @@ view "lan" {
           {% endfor %}
 };
 
+    plugin query "filter-aaaa.so" {
+        filter-aaaa-on-v4 yes;
+        filter-aaaa-on-v6 yes;
+        filter-aaaa { any; };
+    };
+
     recursion yes;
     allow-recursion { any; };
     empty-zones-enable yes;
diff --git a/roles/ns/templates/named.conf.slave.j2 b/roles/ns/templates/named.conf.slave.j2
index c1142a1..cfe4ade 100644
--- a/roles/ns/templates/named.conf.slave.j2
+++ b/roles/ns/templates/named.conf.slave.j2
@@ -20,17 +20,22 @@ masters master-ips {
 
 view "lan" {
         match-clients {
-        {% for item in key_match_clients_lan_master if item.startswith("!key") %}
+        {%- for item in key_match_clients_lan_master if item.startswith("!key") -%}
           {{ item }};
           {% endfor %}
-{% for item in acl_match_clients %}
+{%- for item in acl_match_clients -%}
         {{ item }};
           {% endfor %}
-{% for item in key_match_clients_lan_slave if not item.startswith("!key") %}
+{%- for item in key_match_clients_lan_slave if not item.startswith("!key") -%}
         {{ item }};
           {% endfor %}
 };
 
+    plugin query "filter-aaaa.so" {
+        filter-aaaa-on-v4 yes;
+        filter-aaaa-on-v6 yes;
+        filter-aaaa { any; };
+    };
     recursion yes;
     allow-recursion { any; };
     empty-zones-enable yes;

From 7ee760f5068da7a1acbae9ea6a0537ddb917e53d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es>
Date: Fri, 28 Mar 2025 15:49:06 +0100
Subject: [PATCH 18/34] dns: refs #8552 - indentation jinga master.conf

---
 roles/ns/templates/named.conf.master.j2 | 37 +++++++++++++------------
 1 file changed, 19 insertions(+), 18 deletions(-)

diff --git a/roles/ns/templates/named.conf.master.j2 b/roles/ns/templates/named.conf.master.j2
index e923cc5..3d0a2e0 100644
--- a/roles/ns/templates/named.conf.master.j2
+++ b/roles/ns/templates/named.conf.master.j2
@@ -15,17 +15,17 @@ options {
 };
 
 view "lan" {
-        match-clients {
-        {% for item in key_match_clients_lan_master if item.startswith("!key") %}
-          {{ item }};
-          {% endfor %}
+    match-clients {
+{% for item in key_match_clients_lan_master if item.startswith("!key") %}
+        {{ item }};
+{% endfor %}
 {% for item in acl_match_clients %}
         {{ item }};
-          {% endfor %}
+{% endfor %}
 {% for item in key_match_clients_lan_master if not item.startswith("!key") %}
         {{ item }};
-          {% endfor %}
-};
+{% endfor %}
+    };
 
     plugin query "filter-aaaa.so" {
         filter-aaaa-on-v4 yes;
@@ -40,14 +40,14 @@ view "lan" {
 
     include "/etc/bind/named.conf.default-zones";
 
-    {% for zone in bind_zones.lan %}
+{% for zone in bind_zones.lan %}
     zone "{{ zone.name }}" {
         type master;
         forwarders {};
         allow-update { key {{ zone.key }}; };
         file "{{ zone.file }}";
     };
-    {% endfor %}
+{% endfor %}
 };
 
 view "wan" {
@@ -60,25 +60,26 @@ view "wan" {
     notify explicit;
 
     also-notify {
-    {% for entry in bind_also_notify %}
+{% for entry in bind_also_notify %}
         {{ entry.ip }} key {{ entry.key }};
-    {% endfor %}
+{% endfor %}
     };
 
-    {% for zone in bind_zones.wan %}
-    {% if zone.in_view is defined %}
-    {% for z in zone.in_view %}
+{% for zone in bind_zones.wan %}
+{% if zone.in_view is defined %}
+{% for z in zone.in_view %}
     zone "{{ z }}" {
         in-view "lan";
     };
-    {% endfor %}
-    {% else %}
+{% endfor %}
+{% else %}
     zone "{{ zone.name }}" {
         type master;
         forwarders {};
         allow-update { key {{ zone.key }}; };
         file "{{ zone.file }}";
     };
-    {% endif %}
-    {% endfor %}
+{% endif %}
+{% endfor %}
 };
+

From 0e073c7ba12b7e650cd1ff14f7f66341bf892f9a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es>
Date: Wed, 2 Apr 2025 11:34:17 +0200
Subject: [PATCH 19/34] vpn: refs #8748 - add conntrack iptables default block

---
 roles/ipsec/defaults/main.yml | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/roles/ipsec/defaults/main.yml b/roles/ipsec/defaults/main.yml
index c8b1cd0..a7d3b9d 100644
--- a/roles/ipsec/defaults/main.yml
+++ b/roles/ipsec/defaults/main.yml
@@ -24,6 +24,13 @@ mangle_block: |
   -A PREROUTING -p tcp -m policy --dir in --pol ipsec -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360
   -A POSTROUTING -p tcp -m policy --dir out --pol ipsec -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360
   COMMIT
+  *filter
+  :INPUT ACCEPT [0:0]
+  :FORWARD ACCEPT [0:0]
+  :OUTPUT ACCEPT [0:0]
+  -A FORWARD -m conntrack --ctstate NEW,RELATED,ESTABLISHED -j ACCEPT
+  -A FORWARD -m conntrack --ctstate INVALID -j LOG --log-prefix "CT INVALID: "
+  COMMIT
 config_and_logrotate:
   - { src: vn.conf, dest: '/etc/strongswan.d/vn.conf' }
   - { src: charon, dest: '/etc/logrotate.d/charon' }

From 3fd2bcd6d0535a37b046701360c63d063b2ea5a3 Mon Sep 17 00:00:00 2001
From: Juan Ferrer <juan@verdnatura.es>
Date: Wed, 2 Apr 2025 15:35:47 +0000
Subject: [PATCH 20/34] Update roles/db/templates/conf/z99-local.cnf

---
 roles/db/templates/conf/z99-local.cnf | 1 +
 1 file changed, 1 insertion(+)

diff --git a/roles/db/templates/conf/z99-local.cnf b/roles/db/templates/conf/z99-local.cnf
index a434496..ad99d65 100644
--- a/roles/db/templates/conf/z99-local.cnf
+++ b/roles/db/templates/conf/z99-local.cnf
@@ -1,6 +1,7 @@
 [mysqld]
 
 server-id                                   = {{ serverid }}
+#read_only                                  = ON
 #bind-address                               = 127.0.0.1
 #event-scheduler                            = OFF
 #skip-log-bin

From 54733ee4fe44b85e8f0522abd844336775ce0fdb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es>
Date: Fri, 4 Apr 2025 13:04:34 +0200
Subject: [PATCH 21/34] db: refs #8140 - add new script to change server rol

---
 roles/db/files/scripts/role-change.sh | 123 ++++++++++++++++++++++++++
 1 file changed, 123 insertions(+)
 create mode 100644 roles/db/files/scripts/role-change.sh

diff --git a/roles/db/files/scripts/role-change.sh b/roles/db/files/scripts/role-change.sh
new file mode 100644
index 0000000..1ba3e0b
--- /dev/null
+++ b/roles/db/files/scripts/role-change.sh
@@ -0,0 +1,123 @@
+#!/bin/bash
+# Script to manage MariaDB events during replication role changes.
+#
+# Author: Xavi Lleó & GhatGPT ®
+# Copyright (c) 2025 Verdnatura S.L. All rights reserved.
+# Version: 1.4.0
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# 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 <https://www.gnu.org/licenses/>.
+#
+# Description:
+# This script toggles MariaDB events during replication role changes.
+# It supports three modes:
+#   --promote : Re-enable events previously marked SLAVESIDE_DISABLED
+#   --demote  : Disable currently ENABLED events using DISABLE ON SLAVE
+#   --status  : Display current status of all events
+# All actions are logged to /var/log/mariadb_admin.log.
+
+set -euo pipefail
+
+LOG_FILE="/var/log/mariadb_admin.log"
+TIMESTAMP="$(date '+%F %T')"
+
+log() {
+    echo "$TIMESTAMP - $1" | tee -a "$LOG_FILE"
+}
+
+disable_scheduler() {
+    log "Disabling event_scheduler..."
+    if mysql -e "SET GLOBAL event_scheduler = OFF"; then
+        log "Successfully disabled event_scheduler"
+    else
+        log "ERROR: Failed to disable event_scheduler"
+        exit 1
+    fi
+}
+
+enable_scheduler() {
+    log "Re-enabling event_scheduler..."
+    if mysql -e "SET GLOBAL event_scheduler = ON"; then
+        log "Successfully re-enabled event_scheduler"
+    else
+        log "ERROR: Failed to re-enable event_scheduler"
+        exit 1
+    fi
+}
+
+final_status() {
+    log "Final event_scheduler status:"
+    mysql --table -e "SHOW VARIABLES LIKE 'event_scheduler';" | tee -a "$LOG_FILE"
+
+    log "Final state of all events:"
+    mysql --table -e "SELECT db, name, status FROM mysql.event;" | tee -a "$LOG_FILE"
+}
+
+if [[ $# -ne 1 ]]; then
+    echo "Usage: $0 --promote | --demote | --status"
+    exit 1
+fi
+
+MODE="$1"
+
+case "$MODE" in
+    --status)
+        log "Fetching current event_scheduler status:"
+        mysql --table -e "SHOW VARIABLES LIKE 'event_scheduler';" | tee -a "$LOG_FILE"
+
+        log "Fetching current event status:"
+        mysql --table -e "SELECT db, name, status FROM mysql.event;" | tee -a "$LOG_FILE"
+        ;;
+    
+    --promote)
+        disable_scheduler
+
+        log "Promoting: Enabling events with SLAVESIDE_DISABLED status..."
+        mysql --raw --silent -e "SELECT db, name FROM mysql.event WHERE status = 'SLAVESIDE_DISABLED'" | \
+        awk '{ gsub("`", "``", $1); gsub("`", "``", $2); print "`"$1"`.`"$2"`" }' | \
+        while read -r event; do
+            if mysql -e "ALTER EVENT $event ENABLE"; then
+                log "Enabled event: $event"
+            else
+                log "ERROR: Failed to enable event: $event"
+            fi
+        done
+
+        enable_scheduler
+        final_status
+        ;;
+
+    --demote)
+        disable_scheduler
+
+        log "Demoting: Disabling events currently ENABLED..."
+        mysql --raw --silent -e "SELECT db, name FROM mysql.event WHERE status = 'ENABLED'" | \
+        awk '{ gsub("`", "``", $1); gsub("`", "``", $2); print "`"$1"`.`"$2"`" }' | \
+        while read -r event; do
+            if mysql -e "ALTER EVENT $event DISABLE ON SLAVE"; then
+                log "Disabled event: $event"
+            else
+                log "ERROR: Failed to disable event: $event"
+            fi
+        done
+
+        enable_scheduler
+        final_status
+        ;;
+
+    *)
+        echo "Invalid mode: $MODE"
+        echo "Usage: $0 --promote | --demote | --status"
+        exit 1
+        ;;
+esac

From 1f745a1c8c14a27369f576e3c055de221c046726 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es>
Date: Fri, 4 Apr 2025 13:38:56 +0200
Subject: [PATCH 22/34] db: refs #8140 - clean party

---
 roles/db/defaults/main.yaml              |  3 +--
 roles/db/files/scripts/events-demote.sh  | 16 ----------------
 roles/db/files/scripts/events-promote.sh | 15 ---------------
 3 files changed, 1 insertion(+), 33 deletions(-)
 delete mode 100755 roles/db/files/scripts/events-demote.sh
 delete mode 100755 roles/db/files/scripts/events-promote.sh

diff --git a/roles/db/defaults/main.yaml b/roles/db/defaults/main.yaml
index 7dd354f..81e7852 100644
--- a/roles/db/defaults/main.yaml
+++ b/roles/db/defaults/main.yaml
@@ -29,8 +29,7 @@ required_mariabackup_files_and_scripts:
   - { src: scripts/check-memory.sh, dest: /root/scripts/check-memory.sh, mode: u=rwx,g=rx,o=rx }
   - { src: scripts/export-privs.sh, dest: /root/scripts/export-privs.sh, mode: u=rwx,g=rx,o=rx }
   - { src: scripts/mysqltuner.pl, dest: /root/scripts/mysqltuner.pl, mode: u=rwx,g=rx,o=rx }
-  - { src: scripts/events-promote.sh, dest: /root/scripts/events-promote.sh, mode: u=rwx,g=rx,o=rx }
-  - { src: scripts/events-demote.sh, dest: /root/scripts/events-demote.sh, mode: u=rwx,g=rx,o=rx }
+  - { src: scripts/role-change.sh, dest: /root/scripts/role-change.sh, mode: u=rwx,g=rx,o=rx }
   - { src: scripts/README.md, dest: /root/scripts/README.md, mode: u=rw,g=r,o=r }
   - { src: scripts/scheduler-log.sh, dest: /root/scripts/scheduler-log.sh, mode: u=rwx,g=rx,o=rx }
 configuration_files:
diff --git a/roles/db/files/scripts/events-demote.sh b/roles/db/files/scripts/events-demote.sh
deleted file mode 100755
index 4856f4f..0000000
--- a/roles/db/files/scripts/events-demote.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/bash
-
-mysql -e "SET GLOBAL event_scheduler = OFF"
-
-echo "SELECT db, name FROM mysql.event WHERE status = 'ENABLED'" | mysql --raw --silent | \
-awk '{
-	gsub("`", "``", $1);
-	gsub("`", "``", $2);
-	print "`"$1"`.`"$2"`";
-}' | \
-while read event; do
-	mysql -e "ALTER EVENT $event DISABLE ON SLAVE"
-done
-
-mysql -e "SET GLOBAL event_scheduler = ON"
-
diff --git a/roles/db/files/scripts/events-promote.sh b/roles/db/files/scripts/events-promote.sh
deleted file mode 100755
index d4b5b9f..0000000
--- a/roles/db/files/scripts/events-promote.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/bash
-
-mysql -e "SET GLOBAL event_scheduler = OFF"
-
-echo "SELECT db, name FROM mysql.event WHERE status = 'SLAVESIDE_DISABLED'" | mysql --raw --silent | \
-awk '{
-	gsub("`", "``", $1);
-	gsub("`", "``", $2);
-	print "`"$1"`.`"$2"`";
-}' | \
-while read event; do
-	mysql -e "ALTER EVENT $event ENABLE"
-done
-
-mysql -e "SET GLOBAL event_scheduler = ON"

From 7a925b67c4454c824ed08b463afd6b319c74a50b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es>
Date: Fri, 4 Apr 2025 14:27:11 +0200
Subject: [PATCH 23/34] db: refs #8140 - clean festival

---
 roles/db/defaults/main.yaml | 5 +++++
 roles/db/tasks/mariadb.yml  | 6 ++++++
 2 files changed, 11 insertions(+)

diff --git a/roles/db/defaults/main.yaml b/roles/db/defaults/main.yaml
index 81e7852..61f6c73 100644
--- a/roles/db/defaults/main.yaml
+++ b/roles/db/defaults/main.yaml
@@ -44,3 +44,8 @@ downloads:
   - url: https://repo.percona.com/apt/percona-release_latest.generic_all.deb
     dest: /tmp/percona-release_latest.generic_all.deb
     mode: u=rw,g=r,o=r
+clean_config_and_scripts:
+  - { path: /root/scripts/events-promote.sh }
+  - { path: /root/scripts/events-demote.sh }
+  - { path: /root/scripts/promote-master.sh }
+  - { path: /root/scripts/promote-slave.sh }
\ No newline at end of file
diff --git a/roles/db/tasks/mariadb.yml b/roles/db/tasks/mariadb.yml
index 806125a..db09ac0 100644
--- a/roles/db/tasks/mariadb.yml
+++ b/roles/db/tasks/mariadb.yml
@@ -122,3 +122,9 @@
 - name: Mount all filesystems from /etc/fstab
   command: mount -a
   when: fstab.changed
+
+- name: Clean old configs or scripts
+  file:
+    path: "{{ item.path }}"
+    state: absent
+  loop: "{{ clean_config_and_scripts }}"

From 772a3f3fa2a23fdae93ddaf3e4fdacd850049f61 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Xavi=20Lle=C3=B3=20Tom=C3=A1s?= <xavi@verdnatura.es>
Date: Fri, 4 Apr 2025 15:09:03 +0200
Subject: [PATCH 24/34] db: refs #8140 - variable local-backup

---
 roles/db/templates/mariabackup/apply.config.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/roles/db/templates/mariabackup/apply.config.sh b/roles/db/templates/mariabackup/apply.config.sh
index c74176b..190a00a 100755
--- a/roles/db/templates/mariabackup/apply.config.sh
+++ b/roles/db/templates/mariabackup/apply.config.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # Bacula directory for restore
-baculaDir=/mnt/mysqldata/bacula-restore
+baculaDir=/mnt/local-backup/bacula-restore
 
 # Database branch name
 dbBranch={{ db.branch  }}

From a0a0026f6ef7bc029662fdf36473e8f119d37c18 Mon Sep 17 00:00:00 2001
From: Juan Ferrer <juan@verdnatura.es>
Date: Fri, 4 Apr 2025 16:25:15 +0000
Subject: [PATCH 25/34] Update roles/db/templates/conf/z99-local.cnf

---
 roles/db/templates/conf/z99-local.cnf | 1 +
 1 file changed, 1 insertion(+)

diff --git a/roles/db/templates/conf/z99-local.cnf b/roles/db/templates/conf/z99-local.cnf
index ad99d65..390df19 100644
--- a/roles/db/templates/conf/z99-local.cnf
+++ b/roles/db/templates/conf/z99-local.cnf
@@ -1,6 +1,7 @@
 [mysqld]
 
 server-id                                   = {{ serverid }}
+#log_slave_updates                          = ON
 #read_only                                  = ON
 #bind-address                               = 127.0.0.1
 #event-scheduler                            = OFF

From 5e5aed051f1c5e2cfc705da59b0f7f1cbf9a40db Mon Sep 17 00:00:00 2001
From: Juan Ferrer Toribio <juan@verdnatura.es>
Date: Sun, 6 Apr 2025 06:46:43 +0200
Subject: [PATCH 26/34] fix: refs #8140 fix call to role-change script

---
 roles/db/files/mariabackup/bacula-after.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/roles/db/files/mariabackup/bacula-after.sh b/roles/db/files/mariabackup/bacula-after.sh
index 6857ed7..6cbaa68 100755
--- a/roles/db/files/mariabackup/bacula-after.sh
+++ b/roles/db/files/mariabackup/bacula-after.sh
@@ -46,7 +46,7 @@ fi
 
 
 echo "Promoting Events."
-"/root/scripts/events-promote.sh"
+"/root/scripts/role-change.sh --promote"
 
 for node in "${dbClusterSiblings[@]}"; do
 	ssh root@$node service mysql start

From 9c2ff170a0f2307a55e31e7f3304cd36bc4088a6 Mon Sep 17 00:00:00 2001
From: Juan Ferrer Toribio <juan@verdnatura.es>
Date: Sun, 6 Apr 2025 18:20:30 +0200
Subject: [PATCH 27/34] feat: refs #8869 differential backups

---
 roles/db/defaults/main.yaml                   | 12 +--
 roles/db/files/mariabackup/bacula-after.sh    |  4 +-
 roles/db/files/mariabackup/bacula-before.sh   | 31 -------
 roles/db/files/mariabackup/config.sh          |  2 +
 roles/db/files/mariabackup/inc-backup.sh      | 38 --------
 roles/db/files/mariabackup/make-backup.sh     | 89 +++++++++++++++++++
 roles/db/files/mariabackup/restore-backup.sh  |  2 +-
 roles/db/tasks/mariadb.yml                    |  2 +-
 .../db/templates/mariabackup/apply.config.sh  |  3 -
 roles/db/templates/mariabackup/my.cnf         |  4 +-
 10 files changed, 104 insertions(+), 83 deletions(-)
 delete mode 100644 roles/db/files/mariabackup/bacula-before.sh
 delete mode 100644 roles/db/files/mariabackup/inc-backup.sh
 create mode 100644 roles/db/files/mariabackup/make-backup.sh

diff --git a/roles/db/defaults/main.yaml b/roles/db/defaults/main.yaml
index 61f6c73..4828592 100644
--- a/roles/db/defaults/main.yaml
+++ b/roles/db/defaults/main.yaml
@@ -22,7 +22,7 @@ required_directories:
   - { path: /etc/systemd/system/mariadb.service.d, owner: root, group: root, mode: 'u=rwx,g=rx,o=rx' }
 required_mariabackup_files_and_scripts:
   - { src: mysql-flush.sh, dest: /etc/qemu/fsfreeze-hook.d/mysql-flush.sh, mode: u=rwx,g=rx,o=rx }  
-  - { src: mariabackup/bacula-before.sh, dest: /root/mariabackup/bacula-before.sh, mode: u=rwx,g=rx,o=rx }
+  - { src: mariabackup/make-backup.sh, dest: /root/mariabackup/make-backup.sh, mode: u=rwx,g=rx,o=rx }
   - { src: mariabackup/config.sh, dest: /root/mariabackup/config.sh, mode: u=rwx,g=rx,o=x }
   - { src: mariabackup/inc-backup.sh, dest: /root/mariabackup/inc-backup.sh, mode: u=rwx,g=rx,o=rx }
   - { src: mariabackup/restore-backup.sh, dest: /root/mariabackup/restore-backup.sh, mode: u=rwx,g=rx,o=rx }
@@ -45,7 +45,9 @@ downloads:
     dest: /tmp/percona-release_latest.generic_all.deb
     mode: u=rw,g=r,o=r
 clean_config_and_scripts:
-  - { path: /root/scripts/events-promote.sh }
-  - { path: /root/scripts/events-demote.sh }
-  - { path: /root/scripts/promote-master.sh }
-  - { path: /root/scripts/promote-slave.sh }
\ No newline at end of file
+  - /root/scripts/events-promote.sh
+  - /root/scripts/events-demote.sh
+  - /root/scripts/promote-master.sh
+  - /root/scripts/promote-slave.sh
+  - /root/scripts/inc-backup.sh
+  - /root/scripts/bacula-before.sh
\ No newline at end of file
diff --git a/roles/db/files/mariabackup/bacula-after.sh b/roles/db/files/mariabackup/bacula-after.sh
index 6cbaa68..d38e399 100755
--- a/roles/db/files/mariabackup/bacula-after.sh
+++ b/roles/db/files/mariabackup/bacula-after.sh
@@ -5,12 +5,12 @@ myDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
 . "$myDir/config.sh"
 . "$myDir/apply.config.sh"
 
-pattern="$baculaDir/mnt/local-backup/*_full.gz"
+pattern="$backupDir/*_full.gz"
 files=($pattern)
 backupFile="${files[0]}"
 
 "$myDir/restore-backup.sh" "$backupFile"
-rm -r "$baculaDir"
+rm -r "$backupDir/"*
 
 if [[ "${#dbClusterSiblings[@]}" -gt "0" ]]; then
 	for node in "${dbClusterSiblings[@]}"; do
diff --git a/roles/db/files/mariabackup/bacula-before.sh b/roles/db/files/mariabackup/bacula-before.sh
deleted file mode 100644
index f4723a3..0000000
--- a/roles/db/files/mariabackup/bacula-before.sh
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/bash
-# https://mariadb.com/kb/en/mariabackup/
-set -e
-
-myDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-. "$myDir/config.sh"
-
-todayDir="$(date +%Y-%m-%d)"
-backupName="${todayDir}_$(date +"%H-%M")_full"
-backupFile="$backupDir/$backupName.gz"
-
-if [ -d "$backupDir" ]; then
-	rm -rf "$backupDir/"*
-fi
-
-ulimit -n 8192
-mariabackup \
-	--defaults-extra-file="$myDir/my.cnf" \
-	--backup \
-	--extra-lsndir="$backupDir/$backupName" \
-	--history="$todayDir" \
-	--stream=xbstream \
-	--parallel=4 \
-	2>> "$logFile" \
-	| pigz -p 12 \
-	> "$backupFile"
-
-if [ $? != "0" ]; then
-	echo "An error ocurred during backup, please take a look at log file: $logFile"
-	exit 1
-fi
diff --git a/roles/db/files/mariabackup/config.sh b/roles/db/files/mariabackup/config.sh
index bb61fd8..3aacc00 100644
--- a/roles/db/files/mariabackup/config.sh
+++ b/roles/db/files/mariabackup/config.sh
@@ -18,3 +18,5 @@ restoreDir=/mnt/mysqldata/mysql-restore
 # Directory of MySQL data
 dataDir=/mnt/mysqldata/mysql
 
+# Number of procs created by pigz
+pigzProcs=12
diff --git a/roles/db/files/mariabackup/inc-backup.sh b/roles/db/files/mariabackup/inc-backup.sh
deleted file mode 100644
index c6d6e91..0000000
--- a/roles/db/files/mariabackup/inc-backup.sh
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/bin/bash
-# https://mariadb.com/kb/en/incremental-backup-and-restore-with-mariabackup/
-set -e
-
-myDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-. "$myDir/config.sh"
-
-todayDir="$(date +%Y-%m-%d)"
-todayPath="$historyDir/$todayDir"
-
-pattern="$todayPath/${todayDir}_??-??_full.xb.gz.enc"
-files=($pattern)
-backupFile="${files[0]}"
-backupBase=$(basename -- "$backupFile")
-backupName="${backupBase%%.*}"
-
-incrementalName="${todayDir}_$(date +"%H-%M")_incremental"
-incrementalFile="$backupDir/${incrementalName}.xb.gz.enc"
-
-ulimit -n 24098
-mariabackup \
-	--defaults-extra-file="$myDir/my.cnf" \
-	--backup \
-	--incremental-basedir="$backupDir/$backupName" \
-	--extra-lsndir="$backupDir/$incrementalName" \
-	--incremental-history-name="$todayDir" \
-	2>> "$logFile" \
-	| gzip \
-	| openssl enc -aes-256-cbc -pbkdf2 -kfile "$myDir/xbcrypt.key" \
-	> "$incrementalFile"
-
-if [ $? != "0" ]; then
-	echo "An error ocurred during backup, please take a look at log file: $logFile"
-	exit 1
-fi
-
-cp "$incrementalFile" "$todayPath"
-cp -r "$backupDir/$incrementalName" "$todayPath"
diff --git a/roles/db/files/mariabackup/make-backup.sh b/roles/db/files/mariabackup/make-backup.sh
new file mode 100644
index 0000000..db9d503
--- /dev/null
+++ b/roles/db/files/mariabackup/make-backup.sh
@@ -0,0 +1,89 @@
+#!/bin/bash
+# https://mariadb.com/kb/en/mariabackup/
+# https://mariadb.com/kb/en/mariabackup-options/
+# https://mariadb.com/kb/en/incremental-backup-and-restore-with-mariabackup/
+# https://mariadb.com/kb/en/setting-up-a-replica-with-mariabackup/
+set -e
+
+myDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+. "$myDir/config.sh"
+
+level=$1
+historyName=$2
+todayDir="$(date +%Y-%m-%d)"
+
+usage() {
+	echo "Usage: $0 <Full|Differential|Incremental> [historyName]"
+	exit 1
+}
+backup_error() {
+	echo "An error ocurred during backup, please take a look at log file: $logFile"
+	exit 2
+}
+
+if [ -z "$level" ]; then
+	usage
+fi
+
+case "$level" in
+	Full)
+		backupName="${todayDir}_$(date +"%H-%M")_full"
+		backupFile="$backupDir/$backupName.gz"
+
+		if [ -d "$backupDir" ]; then
+			rm -rf "$backupDir/"*
+		fi
+		if [ -z "$historyName" ]; then
+			historyName=$backupName
+		fi
+
+		ulimit -n 8192
+		mariabackup \
+			--defaults-extra-file="$myDir/my.cnf" \
+			--backup \
+			--history="$historyName" \
+			--extra-lsndir="$backupDir/$backupName" \
+			--slave-info \
+			--safe-slave-backup \
+			2>> "$logFile" \
+			| pigz -p "$pigzProcs" \
+			> "$backupFile"
+
+		if [ $? != "0" ]; then
+			backup_error
+		fi
+		;;
+	Differential|Incremental)
+		pattern="$backupDir/${todayDir}_??-??_full.gz"
+		files=($pattern)
+		backupFile="${files[0]}"
+		backupBase=$(basename -- "$backupFile")
+		backupName="${backupBase%%.*}"
+
+		incrementalName="${backupName%%_full}_diff_$(date +"%H-%M")"
+		incrementalFile="$backupDir/${incrementalName}.gz"
+
+		if [ -z "$historyName" ]; then
+			historyName=$incrementalName
+		fi
+
+		ulimit -n 24098
+		mariabackup \
+			--defaults-extra-file="$myDir/my.cnf" \
+			--backup \
+			--history="$historyName" \
+			--extra-lsndir="$backupDir/$incrementalName" \
+			--incremental-basedir="$backupDir/$backupName" \
+			--slave-info \
+			--safe-slave-backup \
+			2>> "$logFile" \
+			| pigz -p "$pigzProcs" \
+			> "$incrementalFile"
+
+		if [ $? != "0" ]; then
+			backup_error
+		fi
+		;;
+	*)
+		usage
+esac
diff --git a/roles/db/files/mariabackup/restore-backup.sh b/roles/db/files/mariabackup/restore-backup.sh
index 73d06e2..ecf2fd7 100644
--- a/roles/db/files/mariabackup/restore-backup.sh
+++ b/roles/db/files/mariabackup/restore-backup.sh
@@ -28,7 +28,7 @@ mkdir -p "$restoreDir"
 echo "$(formatted_date)"
 echo "Decompresing backup."
 pigz --decompress --processes 4 --stdout "$backupFile" \
-	| mbstream --extract --parallel=4 --directory="$restoreDir"
+	| mbstream --extract --parallel=6 --directory="$restoreDir"
 
 echo "Preparing backup."
 mariabackup \
diff --git a/roles/db/tasks/mariadb.yml b/roles/db/tasks/mariadb.yml
index db09ac0..4b204cd 100644
--- a/roles/db/tasks/mariadb.yml
+++ b/roles/db/tasks/mariadb.yml
@@ -125,6 +125,6 @@
 
 - name: Clean old configs or scripts
   file:
-    path: "{{ item.path }}"
+    path: "{{ item }}"
     state: absent
   loop: "{{ clean_config_and_scripts }}"
diff --git a/roles/db/templates/mariabackup/apply.config.sh b/roles/db/templates/mariabackup/apply.config.sh
index 190a00a..ca604ae 100755
--- a/roles/db/templates/mariabackup/apply.config.sh
+++ b/roles/db/templates/mariabackup/apply.config.sh
@@ -1,8 +1,5 @@
 #!/bin/bash
 
-# Bacula directory for restore
-baculaDir=/mnt/local-backup/bacula-restore
-
 # Database branch name
 dbBranch={{ db.branch  }}
 
diff --git a/roles/db/templates/mariabackup/my.cnf b/roles/db/templates/mariabackup/my.cnf
index 7037bef..47207c2 100644
--- a/roles/db/templates/mariabackup/my.cnf
+++ b/roles/db/templates/mariabackup/my.cnf
@@ -2,6 +2,6 @@
 host = localhost
 user = mariabackup
 password = {{ lookup(passbolt, 'mariabackup', folder_parent_id=passbolt_folder).password }}
-use-memory = 1G
-parallel = 4
+use-memory = 4G
+parallel = 8
 stream = xbstream

From 61da00046e1c1a12a9e2fc3bbf1a1715c897eeb2 Mon Sep 17 00:00:00 2001
From: Juan Ferrer Toribio <juan@verdnatura.es>
Date: Sun, 6 Apr 2025 21:12:32 +0200
Subject: [PATCH 28/34] feat: refs #8869 remove previous differential backup

---
 roles/db/files/mariabackup/make-backup.sh | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/roles/db/files/mariabackup/make-backup.sh b/roles/db/files/mariabackup/make-backup.sh
index db9d503..da5ea55 100644
--- a/roles/db/files/mariabackup/make-backup.sh
+++ b/roles/db/files/mariabackup/make-backup.sh
@@ -10,7 +10,7 @@ myDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
 
 level=$1
 historyName=$2
-todayDir="$(date +%Y-%m-%d)"
+curDate="$(date +%Y-%m-%d)"
 
 usage() {
 	echo "Usage: $0 <Full|Differential|Incremental> [historyName]"
@@ -27,7 +27,7 @@ fi
 
 case "$level" in
 	Full)
-		backupName="${todayDir}_$(date +"%H-%M")_full"
+		backupName="${curDate}_$(date +"%H-%M")_full"
 		backupFile="$backupDir/$backupName.gz"
 
 		if [ -d "$backupDir" ]; then
@@ -54,7 +54,7 @@ case "$level" in
 		fi
 		;;
 	Differential|Incremental)
-		pattern="$backupDir/${todayDir}_??-??_full.gz"
+		pattern="$backupDir/${curDate}_??-??_full.gz"
 		files=($pattern)
 		backupFile="${files[0]}"
 		backupBase=$(basename -- "$backupFile")
@@ -63,6 +63,9 @@ case "$level" in
 		incrementalName="${backupName%%_full}_diff_$(date +"%H-%M")"
 		incrementalFile="$backupDir/${incrementalName}.gz"
 
+		if [ "$level" = "Differential" ]; then
+			rm -rf "$backupDir/${curDate}_"??-??_diff_??-??
+		fi
 		if [ -z "$historyName" ]; then
 			historyName=$incrementalName
 		fi

From 5a700d188fa7617edd7f75347bfcb9d6580f9fc9 Mon Sep 17 00:00:00 2001
From: Juan Ferrer Toribio <juan@verdnatura.es>
Date: Sun, 6 Apr 2025 21:13:27 +0200
Subject: [PATCH 29/34] feat: refs #8869 remove previous differential backup

---
 roles/db/files/mariabackup/make-backup.sh | 1 +
 1 file changed, 1 insertion(+)

diff --git a/roles/db/files/mariabackup/make-backup.sh b/roles/db/files/mariabackup/make-backup.sh
index da5ea55..47bad8f 100644
--- a/roles/db/files/mariabackup/make-backup.sh
+++ b/roles/db/files/mariabackup/make-backup.sh
@@ -89,4 +89,5 @@ case "$level" in
 		;;
 	*)
 		usage
+		;;
 esac

From d2f8dc69c2fb3aa477f3313f8ab750158ba2ccba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Xavi=20Lle=C3=B3?= <xavi@verdnatura.es>
Date: Mon, 7 Apr 2025 07:00:53 +0000
Subject: [PATCH 30/34] Actualizar roles/db/files/mariabackup/make-backup.sh

Nota absurda
---
 roles/db/files/mariabackup/make-backup.sh | 1 +
 1 file changed, 1 insertion(+)

diff --git a/roles/db/files/mariabackup/make-backup.sh b/roles/db/files/mariabackup/make-backup.sh
index 47bad8f..3a5e7b1 100644
--- a/roles/db/files/mariabackup/make-backup.sh
+++ b/roles/db/files/mariabackup/make-backup.sh
@@ -3,6 +3,7 @@
 # https://mariadb.com/kb/en/mariabackup-options/
 # https://mariadb.com/kb/en/incremental-backup-and-restore-with-mariabackup/
 # https://mariadb.com/kb/en/setting-up-a-replica-with-mariabackup/
+# Pudo escribir en el fichero, pero no puedo escribir en gitea para dejarte una nota, valgame dior
 set -e
 
 myDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

From a73ec2b34018a7d9086eaeb38b53351bd51c4740 Mon Sep 17 00:00:00 2001
From: Juan Ferrer Toribio <juan@verdnatura.es>
Date: Mon, 7 Apr 2025 09:41:43 +0200
Subject: [PATCH 31/34] doc: refs #8869 make-backup.sh doc added

---
 roles/db/files/mariabackup/make-backup.sh | 35 ++++++++++++++++++++---
 1 file changed, 31 insertions(+), 4 deletions(-)

diff --git a/roles/db/files/mariabackup/make-backup.sh b/roles/db/files/mariabackup/make-backup.sh
index 47bad8f..91e1963 100644
--- a/roles/db/files/mariabackup/make-backup.sh
+++ b/roles/db/files/mariabackup/make-backup.sh
@@ -1,8 +1,35 @@
 #!/bin/bash
-# https://mariadb.com/kb/en/mariabackup/
-# https://mariadb.com/kb/en/mariabackup-options/
-# https://mariadb.com/kb/en/incremental-backup-and-restore-with-mariabackup/
-# https://mariadb.com/kb/en/setting-up-a-replica-with-mariabackup/
+#
+# Author: Juan Ferrer
+# Copyright (c) 2025 Verdnatura S.L. All rights reserved.
+# Version: 0.0.2
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# 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 <https://www.gnu.org/licenses/>.
+#
+# Backups the MariaDB database, it supports three modes:
+#  - Full: Full backup of all DB data, deletes all previous backups.
+#  - Differential: Backup changes since the last full backup, deletes all 
+#    previous differential backups.
+#  - Incremental: Backup changes since the last full backup, same as 
+#    differential, but does not erase any previous backups.
+#
+# References:
+#  - https://mariadb.com/kb/en/mariabackup/
+#  - https://mariadb.com/kb/en/mariabackup-options/
+#  - https://mariadb.com/kb/en/incremental-backup-and-restore-with-mariabackup/
+#  - https://mariadb.com/kb/en/setting-up-a-replica-with-mariabackup/
+#
 set -e
 
 myDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

From e31b19f78bb0eed11cba54ae09a8b8f9543a7898 Mon Sep 17 00:00:00 2001
From: Juan Ferrer Toribio <juan@verdnatura.es>
Date: Mon, 7 Apr 2025 10:20:25 +0200
Subject: [PATCH 32/34] fix: refs #8140 remove role-change script quotes

---
 roles/db/files/mariabackup/bacula-after.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/roles/db/files/mariabackup/bacula-after.sh b/roles/db/files/mariabackup/bacula-after.sh
index d38e399..32bb6b9 100755
--- a/roles/db/files/mariabackup/bacula-after.sh
+++ b/roles/db/files/mariabackup/bacula-after.sh
@@ -46,7 +46,7 @@ fi
 
 
 echo "Promoting Events."
-"/root/scripts/role-change.sh --promote"
+/root/scripts/role-change.sh --promote
 
 for node in "${dbClusterSiblings[@]}"; do
 	ssh root@$node service mysql start

From 3dae1cb8dccb912c6a2110f3f8bd7aaa4e071c86 Mon Sep 17 00:00:00 2001
From: Juan Ferrer Toribio <juan@verdnatura.es>
Date: Mon, 7 Apr 2025 10:52:48 +0200
Subject: [PATCH 33/34] doc: refs #8869 remove deprecated inc-backup script ref

---
 roles/db/defaults/main.yaml | 1 -
 1 file changed, 1 deletion(-)

diff --git a/roles/db/defaults/main.yaml b/roles/db/defaults/main.yaml
index 4828592..6820f53 100644
--- a/roles/db/defaults/main.yaml
+++ b/roles/db/defaults/main.yaml
@@ -24,7 +24,6 @@ required_mariabackup_files_and_scripts:
   - { src: mysql-flush.sh, dest: /etc/qemu/fsfreeze-hook.d/mysql-flush.sh, mode: u=rwx,g=rx,o=rx }  
   - { src: mariabackup/make-backup.sh, dest: /root/mariabackup/make-backup.sh, mode: u=rwx,g=rx,o=rx }
   - { src: mariabackup/config.sh, dest: /root/mariabackup/config.sh, mode: u=rwx,g=rx,o=x }
-  - { src: mariabackup/inc-backup.sh, dest: /root/mariabackup/inc-backup.sh, mode: u=rwx,g=rx,o=rx }
   - { src: mariabackup/restore-backup.sh, dest: /root/mariabackup/restore-backup.sh, mode: u=rwx,g=rx,o=rx }
   - { src: scripts/check-memory.sh, dest: /root/scripts/check-memory.sh, mode: u=rwx,g=rx,o=rx }
   - { src: scripts/export-privs.sh, dest: /root/scripts/export-privs.sh, mode: u=rwx,g=rx,o=rx }

From 15c0cf2cce251f46d3926ec5518af145932433a6 Mon Sep 17 00:00:00 2001
From: Juan Ferrer <juan@verdnatura.es>
Date: Mon, 7 Apr 2025 13:24:14 +0000
Subject: [PATCH 34/34] refs #8869 fix delete differential glob pattern

---
 roles/db/files/mariabackup/make-backup.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/roles/db/files/mariabackup/make-backup.sh b/roles/db/files/mariabackup/make-backup.sh
index 91e1963..41d4c37 100644
--- a/roles/db/files/mariabackup/make-backup.sh
+++ b/roles/db/files/mariabackup/make-backup.sh
@@ -91,7 +91,7 @@ case "$level" in
 		incrementalFile="$backupDir/${incrementalName}.gz"
 
 		if [ "$level" = "Differential" ]; then
-			rm -rf "$backupDir/${curDate}_"??-??_diff_??-??
+			rm -rf "$backupDir/${curDate}_"??-??_diff_??-??{,.gz}
 		fi
 		if [ -z "$historyName" ]; then
 			historyName=$incrementalName