Compare commits

...

166 commits

Author SHA1 Message Date
Alexis Ben Miloud--Josselin 8ad55d9a84 Revert "evolinux-base: Create custom SSH configuration file"
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2743|0|2743|0|:zzz:
gitea/ansible-roles/pipeline/head This commit looks good
This reverts commit f7ceaf2fb6.
2024-05-24 11:48:17 +02:00
Alexis Ben Miloud--Josselin f7ceaf2fb6 evolinux-base: Create custom SSH configuration file
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2746|0|2746|0|:zzz:
gitea/ansible-roles/pipeline/head This commit looks good
2024-05-24 11:39:00 +02:00
Jérémy Lecour fe1e66f79c
Merge branch 'unstable' into stable
All checks were successful
gitea/ansible-roles/pipeline/head This commit looks good
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2745|0|2745|0|:zzz:
gitea/ansible-roles/pipeline/tag This commit looks good
2024-05-15 14:16:44 +02:00
Jérémy Lecour 2e529524f7
Release 24.05
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2745|0|2745|0|:zzz:
gitea/ansible-roles/pipeline/head This commit looks good
2024-05-15 14:16:03 +02:00
Jérémy Lecour f05c8c03c9
evomaintenance: move upstream files into upstream folder 2024-05-15 13:50:32 +02:00
Jérémy Lecour 24f1e72147
evomaintenance: upstream release 24.05 2024-05-15 11:33:44 +02:00
Jérémy Lecour 22f7a6b831
evobackup-client: upstream release 24.05.1 2024-05-14 08:24:57 +02:00
Jérémy Lecour 4128962d05
whitespaces 2024-05-13 10:22:03 +02:00
David Prevot c1dc380e85 Update nodesource repository 2024-05-10 17:50:37 +02:00
Jérémy Lecour 346317081e
evolinux-users: improve SSH configuration 2024-05-07 15:11:09 +02:00
Jérémy Lecour 5be04c36ee
comments 2024-05-07 15:08:42 +02:00
David Prevot e0f81f14d0 Changelog entry for previous commit 2024-05-06 17:02:46 +02:00
David Prevot 63c09e2ae5 apt: use archive.debian.org with Buster 2024-05-06 17:00:50 +02:00
Jérémy Lecour d4343833c9
evobackup-client: fix lib path 2024-05-06 10:33:47 +02:00
Jérémy Lecour cf430f286a
evolinux-base: use long version of grep -r instead of grep -R 2024-05-03 13:24:16 +02:00
Jérémy Lecour 38e498cf9a
apt: add version in mail headers for list-upgradable-held-packages.sh 2024-05-03 10:17:43 +02:00
Jérémy Lecour 5903ff8c81
evolinux-base: improve adding the current user to SSH AllowGroups of AllowUsers 2024-05-02 18:44:30 +02:00
Jérémy Lecour 1d342f6952
apt: add list-upgradable-held-packages.sh 2024-05-02 16:21:13 +02:00
Jérémy Lecour 0397612541
whitespaces 2024-05-02 10:27:40 +02:00
Jérémy Lecour 9be5a18c2e
evobackup-client: upstream release 24.05 2024-05-02 10:27:34 +02:00
Jérémy Lecour 110a4a4282
formatting 2024-04-30 17:44:41 +02:00
Jérémy Lecour 145b279a12
Merge branch 'unstable' into stable
All checks were successful
gitea/ansible-roles/pipeline/head This commit looks good
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2710|0|2710|0|:zzz:
gitea/ansible-roles/pipeline/tag This commit looks good
2024-04-30 17:42:11 +02:00
Jérémy Lecour 6ee1e609ac
Release 24.04 2024-04-30 17:41:15 +02:00
Jérémy Lecour 2ad2ae8521
amend CHANGELOG 2024-04-30 17:38:26 +02:00
Jérémy Lecour e3746d18fb
proftpd: remove whitelist block if feature is disabled 2024-04-30 17:38:14 +02:00
Jérémy Lecour 1d5415237c
sort CHANGELOG 2024-04-30 17:16:22 +02:00
Jérémy Lecour e5bbb601d3
evobackup-client: replace non-functional role with install tasks 2024-04-30 17:08:44 +02:00
Mathieu Trossevin 4c2548b21d
fix(certbot): Fix HAPEE renewal hook 2024-04-26 09:29:15 +02:00
Ludovic Poujol 5d11468327
docker-host: lint 2024-04-18 16:10:26 +02:00
Ludovic Poujol a41e78b556
docker-host: Removed setting docker_conf_use_iptables (iptable usage forced to true 2024-04-18 15:38:11 +02:00
Jérémy Lecour 8cd887ee21
minifirewall: fix task name 2024-04-18 15:18:58 +02:00
Jérémy Lecour 42ad242aaf
vrrpd: configure minifirewall with blocks instead of lines 2024-04-18 15:18:42 +02:00
Jérémy Lecour f8e92d2eeb
haproxy: support bookworm for backport packages 2024-04-18 15:11:34 +02:00
Jérémy Lecour 9a65312190
evolinux-base: disable logcheck monitoring of journald only if journald.logfiles exists 2024-04-18 15:10:01 +02:00
Jérémy Dubois 16394060c9 openvpn: let tasks using openbsd_pkg but commented 2024-04-18 11:11:03 +02:00
Jérémy Dubois f4e6aabe8a openvpn: install packages manually
Because openbsd_pkg module is broken since OpenBSD 7.4 with the version of Ansible we currently use
2024-04-18 11:04:08 +02:00
William Hirigoyen 0ec343766d postfix/amavis: max servers is now 3 (previously 2) 2024-04-17 17:09:12 +02:00
William Hirigoyen d1410e38a1 evolinux-base/logcheck: fix conf patch, journal check was not disabled when asked 2024-04-16 10:25:13 +02:00
mgauthier 7272106bce Merge pull request 'Add option to return the amp values' (#177) from munin_plugin_ipmi_with_amp into unstable
Reviewed-on: #177
2024-04-12 17:04:50 +02:00
Mathieu Gauthier-Pilote d11cf4987b Add option to return the amp values 2024-04-12 17:03:36 +02:00
Jérémy Lecour 2a264dd2bc
redis: replace inline argument with environment variable for the password 2024-04-12 15:54:20 +02:00
Brice Waegeneire 5708e7205d nrpe: !disk1 exclude filesystem type overlay 2024-04-11 15:48:37 +02:00
Eric Morino 4bbe2f4f72 Delete 'state' option on template for PGDG repo 2024-04-09 09:12:01 +02:00
Jérémy Lecour 7a9be8d6fa
vrrpd : configure and restart minifirewall before starting VRRP 2024-04-04 18:40:39 +02:00
William Hirigoyen 5acb1956f5 packweb: fix old bug (2017!) .orig file created by module patch and taken in account by ProFTPd 2024-03-27 15:17:48 +01:00
Alexis Ben Miloud--Josselin 96504b1deb evolinux-users: Add sudo mvcli for nagios user 2024-03-27 12:13:49 +01:00
William Hirigoyen 56eef89084 nagios-nrpe: create /etc/bash_completion.d if missing 2024-03-22 11:09:06 +01:00
William Hirigoyen b30b7c884a fail2ban: SQLite purge script didn't vacuum as expected + error when vacuum cannot be done 2024-03-19 18:09:56 +01:00
David Prevot fe66ad9c4f Changelog entry for roundcube log change 2024-03-19 17:44:12 +01:00
David Prevot 96c1017b5d roundcube: Use /var/log/roundcube directly
/home/roundcube/log does not even exists…
2024-03-19 17:07:43 +01:00
David Prevot 23f4f9690f roundcube: Use /var/log/roundcube directly
/var/lib/roundcube/logs is already a symlink to /var/log/roundcube.
2024-03-19 16:54:14 +01:00
Jérémy Lecour d758afdd4b
autosysadmin-agent: upstream release 24.03.2 2024-03-19 08:19:31 +01:00
Ludovic Poujol ae2e447bc4
evolinux-base: Add new variable to disable global customisation of bash config 2024-03-18 15:30:23 +01:00
Eric Morino 8e3724d5e7 Add documentation for add account proftpd with ips whitelist 2024-03-18 09:50:43 +01:00
Eric Morino ff19df2444 Add proftpd_sftp_enable_user_whitelist : False by default 2024-03-18 09:24:13 +01:00
Eric Morino eda69725d5 proftpd: add whitelist ip in virtualhost sftp 2024-03-15 09:20:32 +01:00
Alexis Ben Miloud--Josselin bc9609ce48 nextcloud: use latest version by default 2024-03-14 10:16:40 +01:00
Ludovic Poujol aca146adbc
memcached: Fix conditions not properly writen (installation was always in multi-instance mode) 2024-03-11 10:54:36 +01:00
Gregory Colpart ccff3b2105 Suppression de fichiers désormais inutilisés (ils sont vides) 2024-03-10 12:38:13 +01:00
Tom David--Broglio 2fe0d25277 correction tasks keepalived 2024-03-07 15:47:39 +01:00
Jérémy Lecour 501f5e7577
autosysadmin-agent: upstream release 24.03.1 2024-03-05 16:54:10 +01:00
Alexis Ben Miloud--Josselin 8b68f03910 certbot: Fix HAProxy renewal hook 2024-03-04 15:00:01 +01:00
Jérémy Lecour 78326e43e8
wording in the CHANGELOG 2024-03-01 09:09:52 +01:00
Jérémy Lecour 2a856d579e
Merge branch 'unstable' into stable
All checks were successful
gitea/ansible-roles/pipeline/head This commit looks good
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2712|0|2712|0|:zzz:
gitea/ansible-roles/pipeline/tag This commit looks good
2024-03-01 09:06:08 +01:00
Jérémy Lecour beea53aa64
Merge branch 'stable' into unstable 2024-03-01 09:04:25 +01:00
Jérémy Lecour 342380876a
Release 24.03 2024-03-01 09:00:49 +01:00
Jérémy Lecour 24cbbf2f54
fix CHANGELOG 2024-03-01 08:55:07 +01:00
Jérémy Lecour 56237bb3c6
evolinux-base: execute autosysadmin-agent and autosysadmin-restart_nrpe roles
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2711|9|2702|6|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/13//ansiblelint">Evolix » ansible-roles » unstable #13</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-03-01 08:35:16 +01:00
Jérémy Lecour abd329b9c1
autosysadmin-restart_nrpe: add role 2024-03-01 08:32:47 +01:00
Jérémy Lecour 037ec9d376
autosysadmin-agent: upstream release 24.03 2024-03-01 08:26:43 +01:00
Jérémy Lecour c333970606
autosysadmin-agent: rename /usr/share/scripts/autosysadmin/{auto,restart} 2024-02-29 19:16:18 +01:00
Jérémy Lecour 10b507adc4
autosysadmin-agent: logs clearing is done weekly
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2708|6|2702|4|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/12//ansiblelint">Evolix » ansible-roles » unstable #12</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-02-29 18:50:14 +01:00
Jérémy Lecour b2e22413bc
autosysadmin-agent: upstream release 24.02.3
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2706|22|2684|5|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/11//ansiblelint">Evolix » ansible-roles » unstable #11</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-02-28 15:40:39 +01:00
William Hirigoyen bec868009c nagios: add option --full to check pressure IO and mem to avoid flaps
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2689|6|2683|6|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/10//ansiblelint">Evolix » ansible-roles » unstable #10</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-02-27 10:33:49 +01:00
David Prevot aea710cb25 redis: Update munin plugin
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2689|4|2685|3|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/9//ansiblelint">Evolix » ansible-roles » unstable #9</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-02-22 09:44:50 +01:00
Ludovic Poujol b0ba70f06c
certbot: Renewal hook for NRPE
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2688|4|2684|6|:+1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/8//ansiblelint">Evolix » ansible-roles » unstable #8</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-02-21 12:27:18 +01:00
Jérémy Dubois 0a4a220bdf openvpn: earlier alert for CA expiration
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2690|4|2686|3|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/7//ansiblelint">Evolix » ansible-roles » unstable #7</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-02-21 10:51:08 +01:00
Jérémy Lecour 282dcb28f4
apt: add comments to deb822 migration scripts
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2689|4|2685|3|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/6//ansiblelint">Evolix » ansible-roles » unstable #6</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-02-20 18:50:39 +01:00
Alexis Ben Miloud--Josselin a0fc763a0c certbot: Utiliser pkey pour tester clé
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2688|3|2685|3|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/5//ansiblelint">Evolix » ansible-roles » unstable #5</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-02-20 16:12:26 +01:00
David Prevot a56e8c27ee lxc-php, php: Update sury PGP key
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2688|4|2684|5|:+1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/4//ansiblelint">Evolix » ansible-roles » unstable #4</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-02-20 13:57:17 +01:00
Jérémy Lecour 56db6e1fbc
apt: add ftp.evolix.org as recognized system source
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2689|0|2689|0|:zzz:
gitea/ansible-roles/pipeline/head This commit looks good
2024-02-20 09:49:41 +01:00
Jérémy Lecour 015cac688e
redis: create sysfs config file if missing 2024-02-20 09:48:58 +01:00
Jérémy Lecour c12c581f63
update CHANGELOG
Some checks failed
gitea/ansible-roles/pipeline/head There was a failure building this commit
2024-02-12 19:07:20 +01:00
Alexis Ben Miloud--Josselin 7c2fd5e394
kvm-host: Add firewall rule for DRBD
Some checks failed
gitea/ansible-roles/pipeline/head There was a failure building this commit
2024-02-12 18:52:29 +01:00
Jérémy Lecour 9402458304
Merge branch 'unstable' into stable
Some checks failed
gitea/ansible-roles/pipeline/head There was a failure building this commit
gitea/ansible-roles/pipeline/tag There was a failure building this commit
2024-02-08 11:08:47 +01:00
Jérémy Lecour cf0fab1e22
Release 24.02.1 2024-02-08 11:08:28 +01:00
Jérémy Lecour 13284645de
fail2ban: fix Ansible syntax
Some checks failed
gitea/ansible-roles/pipeline/head There was a failure building this commit
2024-02-08 11:03:14 +01:00
Jérémy Lecour 2f96151c70
Merge branch 'unstable' into stable
Some checks failed
gitea/ansible-roles/pipeline/head There was a failure building this commit
gitea/ansible-roles/pipeline/tag There was a failure building this commit
2024-02-08 09:48:29 +01:00
Jérémy Lecour d4fcc6f8f4
Release 24.02 2024-02-08 09:27:08 +01:00
Jérémy Lecour eb3aac9d3e
update CHANGELOG 2024-02-08 08:33:49 +01:00
Jérémy Lecour 2e9b6c0680
amavis/ldap: make ldap_suffix mandatory 2024-02-07 16:15:32 +01:00
Jérémy Lecour 0b859fd1a4
dovecot: add variables for LDAP 2024-02-07 16:14:29 +01:00
Jérémy Lecour fe5a61289b
whitespaces 2024-02-07 16:12:32 +01:00
Jérémy Lecour ae665ea178
spamassassin: optimize tasks 2024-02-07 16:01:37 +01:00
Jérémy Lecour d401778024
remount-usr: back to a simpler implementation 2024-02-07 15:43:23 +01:00
Jérémy Lecour 4fb49dd6c9
nginx: clarify intent regarding check mode 2024-02-07 15:39:05 +01:00
Jérémy Lecour ef2e65287e
YAML header 2024-02-07 15:38:29 +01:00
Jérémy Lecour 8af6cdc4d6
apache: create ip_whitelist file if missing 2024-02-07 15:38:28 +01:00
William Hirigoyen 3bb29aa6ba proftpd: fix error when no SSH key is provided 2024-02-07 11:32:41 +01:00
Jérémy Lecour 47d7141a66
evoadmin-mail: apt modules already knows how to download packages 2024-02-06 13:59:28 +01:00
Jérémy Lecour 75650032d4
postfix: default to evolinux_fqdn 2024-02-06 13:33:15 +01:00
Eric Morino 5df27a4bc5 Add variables for generate "ldap_suffix" in amavis role. 2024-02-06 10:29:52 +01:00
Jérémy Lecour 2768b3146f
nginx: simpler regex for settings 2024-02-06 08:46:20 +01:00
Jérémy Lecour 8f86584605
nginx: different way of dealing with check-mode 2024-02-06 08:44:48 +01:00
Jérémy Lecour ba827b79d9
sort CHANGELOG 2024-02-06 08:41:58 +01:00
Jérémy Lecour 12993a8d7c
vrrpd: configure minifirewall 2024-02-06 08:40:55 +01:00
Jérémy Lecour ff233b65a6
remove check-mode protection for handlers
handlers are not supposed to be executed in check-mode since no change should happen in check-mode.
If there is a corner case we should deal with it at the source, not at the handler level.
2024-02-06 08:39:38 +01:00
Jérémy Lecour 8dd9c64cbc
nagios-nrpe: multi-line list 2024-02-06 08:34:22 +01:00
Jérémy Lecour 112bc2133a
dovecot: combine similar tasks 2024-02-06 08:22:18 +01:00
Jérémy Lecour 63745c2697
apt: remove duplicate extension 2024-02-06 08:21:15 +01:00
William Hirigoyen 1f8738fbda postfix: move postfix installation from evolinux-base to postfix role, plus some refactoring
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2696|33|2663|34|:+1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/467//ansiblelint">Evolix » ansible-roles » unstable #467</a>
gitea/ansible-roles/pipeline/head This commit looks good
postfix:
* Move common packages installation in common.yml
* Replace ansible_fqdn by evolinux_fqdn, set postfix_slow_transport_include to false by default (only for packmails and packwebs)
* Remove dependency on evolinux_fqdn var
* Do not overwrite main.cf if it has been modified (except if postfix_force_main_cf)

evolinux-base:
* Move exim4 purge from evolinux-base to postfix role
* Call postfix role call after nagios role (dependency)
2024-02-01 18:00:48 +01:00
William Hirigoyen 554bbaa36f roundcube: set default SMTP port to 25 instead of 587, which failed because of missing SSL conf (local connexion does not need SSL) 2024-02-01 18:00:38 +01:00
William Hirigoyen bc07010aa6 webapps/roundcube & evoadminmail: make roles more idempotent (were failing when played twice) 2024-02-01 18:00:38 +01:00
William Hirigoyen 9f530d78db evolinux-base: addd cert.sh, a small readonly openssl wrapper (testing, not deployed yet) 2024-02-01 18:00:38 +01:00
William Hirigoyen de0a98d693 dovecot: fix missing default mails 2024-02-01 18:00:38 +01:00
Alexis Ben Miloud--Josselin 8741167a80 Revert last commit
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2697|5|2692|5|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/466//ansiblelint">Evolix » ansible-roles » unstable #466</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-02-01 17:04:30 +01:00
Alexis Ben Miloud--Josselin 4c9e4a30cc userlogrotate: Ensure we use a valid group name
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2697|8|2689|7|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/465//ansiblelint">Evolix » ansible-roles » unstable #465</a>
gitea/ansible-roles/pipeline/head This commit looks good
Use user's primary group when user's name is not an existing group.
2024-02-01 11:07:19 +01:00
William Hirigoyen d67e2b122f nagios-nrpe, generateldif: new check_pressure_{cpu,io,mem}
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2696|5|2691|6|:+1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/464//ansiblelint">Evolix » ansible-roles » unstable #464</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-01-31 18:04:11 +01:00
William Hirigoyen 393c1f4ff1 add missing LDAP conf iterate_filter to exclude disabled accounts in users list
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2697|5|2692|4|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/463//ansiblelint">Evolix » ansible-roles » unstable #463</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-01-29 12:04:38 +01:00
David Prevot e14408cb05 apt: follow up from previous commit
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2696|4|2692|6|:+1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/462//ansiblelint">Evolix » ansible-roles » unstable #462</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-01-29 10:07:01 +01:00
David Prevot 1924324c07 apt: No preferences needed for backports
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2698|3|2695|1|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/461//ansiblelint">Evolix » ansible-roles » unstable #461</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-01-29 09:49:36 +01:00
David Prevot d55b2b14bb evolinux-base: tfix
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2696|5|2691|4|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/460//ansiblelint">Evolix » ansible-roles » unstable #460</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-01-29 09:44:57 +01:00
David Prevot b31aa53c81 apt: Improve check mode
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2695|3|2692|5|:+1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/459//ansiblelint">Evolix » ansible-roles » unstable #459</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-01-25 13:59:36 +01:00
William Hirigoyen dae2a25f78 check_free_space: add role; evolinux-base: install check_free_space by default
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2697|20|2677|5|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/458//ansiblelint">Evolix » ansible-roles » unstable #458</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-01-24 17:25:20 +01:00
William Hirigoyen cce7280cd0 fail2ban: add script unban_ip
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2682|3|2679|4|:+1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/457//ansiblelint">Evolix » ansible-roles » unstable #457</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-01-24 15:24:42 +01:00
William Hirigoyen 68d9d3c47c minifirewall: do not open publicly ports except 22222
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2683|3|2680|2|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/456//ansiblelint">Evolix » ansible-roles » unstable #456</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-01-24 11:45:28 +01:00
Alexis Ben Miloud--Josselin 251416f3e8 webapps/nextcloud: Set home directory's mode
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2682|4|2678|5|:+1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/455//ansiblelint">Evolix » ansible-roles » unstable #455</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-01-23 18:00:54 +01:00
Alexis Ben Miloud--Josselin 9b67202acc webapps/nextcloud: Add condition for archive tasks
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2683|7|2676|7|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/454//ansiblelint">Evolix » ansible-roles » unstable #454</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-01-23 16:35:51 +01:00
David Prevot 30bd72614d listupgrade: Fix removal order
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2683|3|2680|2|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/453//ansiblelint">Evolix » ansible-roles » unstable #453</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-01-18 10:14:37 +01:00
David Prevot aa2593f34c Revert "listupgrade: No removal (especially of the just installed cron_file) needed"
This reverts commit 09f951de18.
2024-01-18 10:14:37 +01:00
David Prevot bc19912b71 Revert "listupgrade: try and get rid of duplicate entries"
This reverts commit 531b633d99.
2024-01-18 10:14:36 +01:00
Jérémy Lecour 0c17e4d8fc
sort CHANGELOG
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2682|5|2677|6|:+1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/452//ansiblelint">Evolix » ansible-roles » unstable #452</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-01-18 10:01:46 +01:00
Jérémy Lecour 51280c586a
redis: manage config template inside a block
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2683|18|2665|7|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/451//ansiblelint">Evolix » ansible-roles » unstable #451</a>
gitea/ansible-roles/pipeline/head This commit looks good
This allows to have a coherent block managed by Ansible and extra lines that won't be overwritten.
Eg. : automatically added lines for replication, sentinel groups…
2024-01-18 10:00:44 +01:00
Jérémy Lecour f994e19946
vrrpd: fix typo in switch script 2024-01-18 10:00:43 +01:00
David Prevot 531b633d99 listupgrade: try and get rid of duplicate entries
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2672|7|2665|6|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/450//ansiblelint">Evolix » ansible-roles » unstable #450</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-01-17 17:07:20 +01:00
David Prevot bceb3f5c27 php: drop apt_preferences(5) file for sury (changelog)
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2671|3|2668|6|:+1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/449//ansiblelint">Evolix » ansible-roles » unstable #449</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-01-17 16:51:14 +01:00
David Prevot 422f007e9d php: drop apt_preferences(5) file for sury
It doesn’t work as expected, and all covered cases should already be
available from http://pub.evolix.org/evolix/dists/ (if
$release_name-phpXY is not available, $release should probably be fixed
or the correct suite added to the repository).
2024-01-17 16:49:11 +01:00
Brice Waegeneire 72727a8332 nagios-nrpe: check_phpfpm_multi expand globing of args
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2674|8|2666|7|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/448//ansiblelint">Evolix » ansible-roles » unstable #448</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-01-12 13:38:17 +01:00
Jérémy Lecour f3eb7a4981
listupgrade : old-kernel-removal.sh upstream release 24.01
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2673|6|2667|7|:+1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/447//ansiblelint">Evolix » ansible-roles » unstable #447</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-01-12 11:39:01 +01:00
Jérémy Lecour bca5b9f28c
fail2ban: fix template marker 2024-01-11 17:46:49 +01:00
Jérémy Lecour c9df19e146
warning comment 2024-01-11 17:45:55 +01:00
Jérémy Lecour bf07ef74c3
nginx: take care of « already defined » and « not yet defined » server status suffix in check mode
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2674|15|2659|9|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/446//ansiblelint">Evolix » ansible-roles » unstable #446</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-01-11 16:51:20 +01:00
William Hirigoyen f5d5e84caf dovecot: fix plugin dovecot1
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2668|4|2664|5|:+1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/445//ansiblelint">Evolix » ansible-roles » unstable #445</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-01-09 17:13:22 +01:00
David Prevot e089796c4c evocheck: upstream release 24.01
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2669|5|2664|6|:+1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/444//ansiblelint">Evolix » ansible-roles » unstable #444</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-01-03 17:47:09 +01:00
William Hirigoyen 0a590b6679 nginx: fix multiple fails in check mode
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2670|11|2659|11|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/443//ansiblelint">Evolix » ansible-roles » unstable #443</a>
gitea/ansible-roles/pipeline/head This commit looks good
2024-01-03 11:29:20 +01:00
William Hirigoyen 41897f4c62 bind: improve reload script
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2670|2|2668|2|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/442//ansiblelint">Evolix » ansible-roles » unstable #442</a>
gitea/ansible-roles/pipeline/head This commit looks good
2023-12-29 12:12:39 +01:00
David Prevot 1ac497282c evoadmin-mail: use fixed version for Ansible
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2670|6|2664|6|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/441//ansiblelint">Evolix » ansible-roles » unstable #441</a>
gitea/ansible-roles/pipeline/head This commit looks good
2023-12-22 15:44:40 +01:00
William Hirigoyen 9fb635b45f webapps/evoadmin-mail: package installed via public.evolix.org/evolix repo starting with Bookworm
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2670|6|2664|7|:+1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/440//ansiblelint">Evolix » ansible-roles » unstable #440</a>
gitea/ansible-roles/pipeline/head This commit looks good
(H)acked-By: David Prévot <dprevot+git@evolix.fr>
2023-12-22 15:42:30 +01:00
Mathieu Trossevin c2de4b4cd1
kvm-host: Add LVM filter when needed
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2671|8|2663|5|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/439//ansiblelint">Evolix » ansible-roles » unstable #439</a>
gitea/ansible-roles/pipeline/head This commit looks good
2023-12-22 11:26:08 +01:00
Jérémy Lecour d93eb2495b
sort CHANGELOG
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2668|3|2665|3|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/438//ansiblelint">Evolix » ansible-roles » unstable #438</a>
gitea/ansible-roles/pipeline/head This commit looks good
2023-12-20 15:28:09 +01:00
Jérémy Lecour 046f1411b3
vrrpd: test if interface exists before deleting it 2023-12-20 15:27:07 +01:00
Ludovic Poujol 4a1b94f55d unbound: Add a apt cache validity to enforce an apt update if needed
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2668|4|2664|5|:+1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/437//ansiblelint">Evolix » ansible-roles » unstable #437</a>
gitea/ansible-roles/pipeline/head This commit looks good
2023-12-19 17:55:36 +01:00
Tom David--Broglio 1eb5a47c71 nagios: add dockerd check in nrpe check template
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2669|5|2664|4|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/436//ansiblelint">Evolix » ansible-roles » unstable #436</a>
gitea/ansible-roles/pipeline/head This commit looks good
2023-12-18 19:17:39 +01:00
Tom David--Broglio d4ac4ef7a1 nagios: cleaning nrpe check template 2023-12-18 19:17:39 +01:00
Alexis Ben Miloud--Josselin 70c2d25837 evolinux-base: Check for syntax error in cron.log
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2668|4|2664|5|:+1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/435//ansiblelint">Evolix » ansible-roles » unstable #435</a>
gitea/ansible-roles/pipeline/head This commit looks good
2023-12-18 18:05:36 +01:00
Mathieu Trossevin c0f27426bc
Merge branch 'unstable' of gitea.evolix.org:evolix/ansible-roles into unstable
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2669|6|2663|7|:+1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/434//ansiblelint">Evolix » ansible-roles » unstable #434</a>
gitea/ansible-roles/pipeline/head This commit looks good
2023-12-18 17:47:47 +01:00
Mathieu Trossevin 62c596046d
Add role for automatically deploying autosysadmin 2023-12-18 17:00:51 +01:00
Jérémy Lecour b4c9fcf6f7
mongodb: add gpg key for 7.0
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2670|4|2666|4|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/433//ansiblelint">Evolix » ansible-roles » unstable #433</a>
gitea/ansible-roles/pipeline/head This commit looks good
2023-12-18 16:36:09 +01:00
William Hirigoyen 9e67db57e5 evolinux-base: fix hardware.yml (wrong repo, missing update cache)
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2670|11|2659|8|:-1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/432//ansiblelint">Evolix » ansible-roles » unstable #432</a>
gitea/ansible-roles/pipeline/head This commit looks good
2023-12-18 11:29:40 +01:00
Mathieu Trossevin 0c09763e87
fix(minifirewall): Properly detect old minifirewall versions
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2667|1|2666|2|:+1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/unstable/431//ansiblelint">Evolix » ansible-roles » unstable #431</a>
gitea/ansible-roles/pipeline/head This commit looks good
2023-12-14 16:59:55 +01:00
Jérémy Lecour 198f3fab0a
Merge branch 'unstable' into stable
All checks were successful
gitea/ansible-roles/pipeline/head This commit looks good
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2627|0|2627|0|:zzz:
gitea/ansible-roles/pipeline/tag This commit looks good
2023-10-14 07:38:22 +02:00
Jérémy Lecour a10cff94d0 Merge branch 'unstable' into stable
All checks were successful
gitea/ansible-roles/pipeline/tag This commit looks good
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |2779|4|2775|5|:+1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/stable/8//ansiblelint">Evolix » ansible-roles » stable #8</a>
gitea/ansible-roles/pipeline/head This commit looks good
2023-04-23 10:49:00 +02:00
David Prevot 37e6b14001 listupgrade: Fix removal order
All checks were successful
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |4832|7|4825|8|:+1: Reference build: <a href="https://jenkins.evolix.org/job/gitea/job/ansible-roles/job/stable/5//ansiblelint">Evolix » ansible-roles » stable #5</a>
gitea/ansible-roles/pipeline/head This commit looks good
2023-04-18 09:47:03 +02:00
Jérémy Lecour 7052b7bd1e Merge branch 'unstable' into stable
All checks were successful
gitea/ansible-roles/pipeline/head This commit looks good
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |4832|0|4832|0|:zzz:
gitea/ansible-roles/pipeline/tag This commit looks good
2023-03-16 22:18:04 +01:00
Jérémy Lecour 8e4e77cb8b Merge branch 'unstable' into stable
All checks were successful
gitea/ansible-roles/pipeline/head This commit looks good
Ansible Lint |Total|New|Outstanding|Fixed|Trend |:-:|:-:|:-:|:-:|:-: |4826|0|4826|0|:zzz:
gitea/ansible-roles/pipeline/tag This commit looks good
2023-03-16 14:56:59 +01:00
Jérémy Lecour e1e4f39778 Merge pull request 'Release 22.12' (#165) from unstable into stable
All checks were successful
gitea/ansible-roles/pipeline/head This commit looks good
gitea/ansible-roles/pipeline/tag This commit looks good
Reviewed-on: #165
2022-12-14 12:02:45 +01:00
201 changed files with 9012 additions and 1155 deletions

View file

@ -6,77 +6,215 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
This project does not follow semantic versioning.
The **major** part of the version is the year
The **minor** part changes is the month
The **patch** part changes is incremented if multiple releases happen the same month
The **minor** part is the month
The **patch** part is incremented if multiple releases happen the same month
## [Unreleased]
### Added
* Preliminary work for php83
### Changed
### Fixed
### Removed
### Security
## [24.05] 2024-05-15
### Added
* apt: add list-upgradable-held-packages.sh
### Changed
* evobackup-client: upstream release 24.05.1
* evolinux-base: improve adding the current user to SSH AllowGroups of AllowUsers
* evolinux-users: improve SSH configuration
* evomaintenance: upstream release 24.05
* evomaintenance: move upstream files into upstream folder
### Fixed
* apt: use archive.debian.org with Buster
## [24.04] 2024-04-30
### Added
* proftpd: optional configuration of IP whitelists per groups of users
### Changed
* autosysadmin-agent: upstream release 24.03.2
* evobackup-client: replace non-functional role with install tasks
* evobackup-client: upstream release 24.04.1
* evolinux-base: Add new variable to disable global customisation of bash config
* evolinux-base: Disable logcheck monitoring of journald only if journald.logfiles exists
* evolinux-users: Add sudo mvcli for nagios user
* haproxy: support bookworm for backport packages
* nrpe: !disk1 exclude filesystem type overlay
* postfix/amavis: max servers is now 3 (previously 2)
* roundcube: Use /var/log/roundcube directly
* vrrpd: configure and restart minifirewall before starting VRRP
* vrrpd: configure minifirewall with blocks instead of lines
### Fixed
* certbot: Fix HAPEE renewal hook
* certbot: Fix HAProxy renewal hook
* evolinux-base/logcheck: fix conf patch, journal check was not disabled when asked
* fail2ban: SQLite purge script didn't vacuum as expected + error when vacuum cannot be done
* keepalived: Fix tasks that use file instead of copy
* memcached: Fix conditions not properly writen (installation was always in multi-instance mode)
* nagios-nrpe: create /etc/bash_completion.d if missing
* openvpn: install packages manually, because openbsd_pkg module is broken since OpenBSD 7.4 with the version of Ansible we currently use
* packweb: fix old bug (2017!) .orig file created by module patch and taken in account by ProFTPd
* redis: replace inline argument with environment variable for the password
### Removed
* docker-host: Removed `docker_conf_use_iptables` variable (iptable usage forced to true)
## [24.03] 2024-03-01
### Added
* autosysadmin-agent: upstream release 24.03
* autosysadmin-restart_nrpe: add role
* certbot: Renewal hook for NRPE
* kvm-host: add minifirewall rules if DRBD interface is configured
* proftpd: add whitelist ip
### Changed
* apt: add ftp.evolix.org as recognized system source
* autosysadmin-agent: logs clearing is done weekly
* autosysadmin-agent: rename /usr/share/scripts/autosysadmin/{auto,restart}
* certbot: use pkey to test the key
* evolinux-base: execute autosysadmin-agent and autosysadmin-restart_nrpe roles
* lxc-php, php: Update sury PGP key
* openvpn: earlier alert for CA expiration
* redis: create sysfs config file if missing
* nextcloud: use latest version by default
### Removed
* autosysadmin: replaced by autosysadmin-agent
## [24.02.1] 2024-02-08
### Fixed
* fail2ban: fix Ansible syntax
## [24.02] 2024-02-08
### Added
* Support for PHP 8.3 with bookworm LXC containers
* apt: add task file to install ELTS repository (default: False)
* lxc-php: Allow one to install php83 on Bookworm container
* nagios-nrpe: add check_sentinel for monitoring Redis Sentinel
* webapps/nextcloud: Added var nextcloud_user_uid to enforce uid for nextcloud user
* autosysadmin: Add a role to automatically deploy autosysadmin on evolixisation
* check_free_space: added role
* etc-git: add /var/chroot-bind/etc/bind repo
* webapps/nextcloud: Set ownership and permissions of data directory
* webapps/nextcloud: Add condition for config tasks
* fail2ban: add script unban_ip
* generateldif: new Services for check_pressure_{cpu,io,mem}
* kvm-host: Automatically add an LVM filter when LVM is present
* lxc-php: Allow one to install php83 on Bookworm container
* minifirewall: Fix nagios check for old versions of minifirewall
* mongodb: add gpg key for 7.0
* nagios-nrpe: add check_sentinel for monitoring Redis Sentinel
* nagios-nrpe: new check_pressure_{cpu,io,mem}
* remount-usr: do not try to remount /usr RW if /usr is not a mounted partition
* vrrpd: configure minifirewall
* vrrpd: test if interface exists before deleting it
* webapps/evoadmin-mail: package installed via public.evolix.org/evolix repo starting with Bookworm
* webapps/nextcloud: Add condition for archive tasks
* webapps/nextcloud: Add condition for config tasks
* webapps/nextcloud: Added var nextcloud_user_uid to enforce uid for nextcloud user
* webapps/nextcloud: Set ownership and permissions of data directory
### Changed
* add-vm.sh: allow VM name max length > 20
* amavis: make ldap_suffix mandatory
* apache : fix goaway pattern for bad bots
* apache : rename MaxRequestsPerChild to MaxConnectionsPerChild (new name)
* apache: use backward compatible Redirect directive
* apt: Disable archive repository for Debian 8
* apt: Use the GPG version of the key for Debian 8-9
* bind: Update role for Buster, Bullseye and Bookworm support
* dovecot: add variables for LDAP
* dovecot: Munin plugin conf path is now `/etc/munin/plugin-conf.d/zzz-dovecot` (instead of `z-evolinux-dovecot`)
* evocheck: upstream release 23.11.1
* evocheck: upstream release 24.01
* evolinux-base: dump-server-state upstream release 23.11
* evolinux-base: use separate default config file for rsyslog
* kvmstats: use .capacity instead of .physical for disk size
* ldap: make ldap_suffix mandatory
* listupgrade : old-kernel-removal.sh upstream release 24.01
* log2mail: move custom config in separate file
* lxc: init /etc git repository in lxc container
* mysql: disable performance schema for Debian 8
* nagios: add dockerd check in nrpe check template
* nagios: cleaning nrpe check template
* nagios: rename var `nagios_nrpe_process_processes` into `nagios_nrpe_processes` and check systemd-timesyncd instead of ntpd in Debian 12
* nagios: add option --full to check pressure IO and mem to avoid flaps
* proftpd: in SFTP vhost, enable SSH keys login, enable ed25549 host key for Debian >= 11
* redis: manage config template inside a block, to allow custom modifications outside
* spamassassin: Use spamd starting with Bookworm
* squid: config directory seems to have changed from /etc/squid3 to /etc/squid in Debian 8
* unbound: Add config file to allow configuration reload on Debian 11 and lower
* unbound: Add munin configuration & setup plugin
* unbound: Big cleanup
* unbound: Move generated config file to `/etc/unbound/unbound.conf.d/evolinux.conf`
* unbound: Use root hints provided by debian package dns-root-data instead of downloading them
* vrrpd: replace switch script with custom one (fix MAC issue, use `ip(8)`, shell cleanup…)
* vrrpd: variable to force update the switch script (default: false)
* webapps/nextcloud: Add Ceph volume to fstab
* webapps/nextcloud: Set home directory's mode
### Fixed
* Add php-fpm82 to LDAP when relevant
* Check stat.exists before stat.isdir
* apache: fix MaxRequestsPerChild value to be sync with wiki.e.o
* apt: use archive.debian.org with Stretch
* certbot: fix hook for dovecot when more than one certificate is used (eg. different certificates for POP3 and IMAP)
* dovecot: add missing LDAP conf iterate_filter to exclude disabled accounts in users list (caused « User no longer exists » errors in commands listing users like « doveadm user -u '*' » or « doveadm expunge -u "*" mailbox INBOX savedbefore 7d »).
* dovecot: fix missing default mails
* dovecot: fix plugin dovecot1
* evoadmin-web: Fix PHP version for Bookworm
* evolinux-base: fix hardware.yml (wrong repo, missing update cache)
* evolinux-base: start to install linux-image-cloud-amd64 with Buster
* fail2ban: fix template marker
* minifirewall: ports 25, 53, 443, 993, 995 not opened publicly by default anymore, ports 20, 21, 110, 143 not opened semi-publicly by default anymore.
* nagios: fix default file to monitor for check_clamav_db
* nginx: add "when: not ansible_check_mode" in various tasks to prevent fail in check mode
* nginx: fix mistake between "check_mode: no" and "when: not ansible_check_mode" (fail in check mode)
* nginx: fix mistake between "check_mode: no" and "when: not ansible_check_mode" (fail in check mode)
* nginx: keep indentation
* nginx: take care of « already defined » and « not yet defined » server status suffix in check mode
* php: Bullseye/Sury > Honor the php_version asked in the pub.evolix.org repository
* ProFTPd: set missing default listen IP for SFTP
* php: drop apt_preferences(5) file for sury
* postfix: remove dependency on evolinux_fqdn var
* proftpd: set missing default listen IP for SFTP
* roundcube: set default SMTP port to 25 instead of 587, which failed because of missing SSL conf (local connexion does not need SSL)
* ssl: no not execute haproxy tasks and reload if haproxy is disabled
* webapps/nextcloud: added check that nexctcloud uid is over 3000
* unbound: Add a apt cache validity to enforce an apt update if needed
* webapps/nextcloud: added check that nextcloud uid is over 3000
* webapps/nextcloud: fix Add Ceph volume to fstab : missing UUID= in src
* webapps/nextcloud: fix misplaced gid attribute
* webapps/nextcloud: fix missing gid
* check stat.exists before stat.isdir
* certbot: fix hook for dovecot when more than one certificate is used (eg. different certificates for POP3 and IMAP)
* evolinux-base: start to install linux-image-cloud-amd64 with Buster
* apt: use archive.debian.org with Stretch
* webapps/nextcloud: fix Add Ceph volume to fstab : missing UUID= in src
* webapps/roundcube & evoadminmail: make roles more idempotent (were failing when played twice)
* amavis: Add variables for generate "ldap_suffix"
* proftpd: fix error when no SSH key is provided
### Removed
* evolinux-base: no need to remove update-evobackup-canary from sbin anymore
* evolinux-base: no need to symlink backup-server-state to dump-server-state anymore
### Security
## [23.10] 2023-10-14
### Added

5
amavis/defaults/main.yml Normal file
View file

@ -0,0 +1,5 @@
---
ldap_hostname: "{{ ansible_hostname }}"
ldap_domain: "{{ ansible_domain }}"
ldap_suffix: "dc={{ ldap_hostname }},dc={{ ldap_domain.split('.')[-2] }},dc={{ ldap_domain.split('.')[-1] }}"

View file

@ -6,7 +6,7 @@
- amavisd-new
state: present
tags:
- amavis
- amavis
- name: configure Amavis
ansible.builtin.template:
@ -15,7 +15,7 @@
mode: "0644"
notify: restart amavis
tags:
- amavis
- amavis
- name: Install purge custom cron
ansible.builtin.copy:
@ -23,5 +23,5 @@
dest: /etc/cron.daily/amavis_purge_virusmails
mode: "0755"
tags:
- amavis
- amavis_purge_cron
- amavis
- amavis_purge_cron

View file

@ -39,12 +39,12 @@ $sa_spam_subject_tag = '[SPAM]';
$log_level = 2;
# En fonction besoin/ressources, on a juste le nbre de process
$max_servers = 2;
$max_servers = 3;
$enable_ldap = 1;
$default_ldap = {
hostname => '127.0.0.1', tls => 0,
base => '{{ ldap_suffix }}', scope => 'sub',
base => '{{ ldap_suffix | mandatory }}', scope => 'sub',
query_filter => '(&(mailacceptinggeneralid=%m)(isActive=TRUE))'
};

View file

@ -5,6 +5,7 @@
dest: /etc/apache2/ipaddr_whitelist.conf
line: "Require ip {{ item }}"
state: present
create: yes
loop: "{{ apache_ipaddr_whitelist_present }}"
notify: reload apache
tags:

View file

@ -29,4 +29,7 @@ apt_check_hold_cron_weekday: "*"
apt_check_hold_cron_day: "*"
apt_check_hold_cron_month: "*"
apt_list_upgradable_held_enabled: False
apt_list_upgradable_held_special_time: "weekly"
apt_keyring_dir: "{{ ansible_distribution_major_version is version('12', '<') | ternary('/etc/apt/trusted.gpg.d', '/etc/apt/keyrings') }}"

View file

@ -1,3 +0,0 @@
Package: *
Pin: release a=bookworm-backports
Pin-Priority: 50

View file

@ -1,3 +0,0 @@
Package: *
Pin: release a=bullseye-backports
Pin-Priority: 50

View file

@ -1,3 +0,0 @@
Package: *
Pin: release a=buster-backports
Pin-Priority: 50

View file

@ -1,5 +1,11 @@
#!/usr/bin/env python3
##########
# This script takes a multi-lines input of "oneliner-style" APT sources definitions.
# It converts them into "deb822-style" sources.
# Each generated file will have only one stanza, possibly with multiple Types/Suites/Components
##########
import re
import sys
import os
@ -10,11 +16,16 @@ import apt_pkg
# Order matters !
destinations = {
"debian-security": "security.sources",
".*-backports": "backports.sources",
".debian.org": "system.sources",
"mirror.evolix.org": "system.sources",
"ftp.evolix.org": "system.sources",
"pub.evolix.net": "evolix_public_old.sources.bak",
"pub.evolix.org": "evolix_public.sources",
"artifacts.elastic.co": "elastic.sources",
"download.docker.com": "docker.sources",
"downloads.linux.hpe.com": "hp.sources",
@ -76,6 +87,11 @@ def prepare_sources(lines):
key, value = option.split("=")
options[key] = value
### WARNING ###
# if there are multiple lines with different URIS for a given destination (eg. "system")
# each one will overwrite the previous one
# and the last evaluated will be what remains.
if dest in sources:
sources[dest]["Types"].add(matches["type"])
sources[dest]["URIs"] = matches["uri"]

View file

@ -1,5 +1,11 @@
#!/bin/sh
##########
# This script changes all "one-line" APT sources into "deb822" sources.
# It is responsible for searching and processing the files.
# The actual format migration is done by a python script.
##########
deb822_migrate_script=$(command -v deb822-migration.py)
if [ -z "${deb822_migrate_script}" ]; then
@ -46,4 +52,4 @@ for file in $(find /etc/apt/sources.list.d -mindepth 1 -maxdepth 1 -type f -name
done
echo "${count} file(s) migrated"
exit ${rc}
exit ${rc}

View file

@ -1,3 +0,0 @@
Package: *
Pin: release a=jessie-backports
Pin-Priority: 50

View file

@ -0,0 +1,127 @@
#!/bin/bash
#####
# This script will send an email if some packages are on hold
# but have available updates.
#####
readonly VERSION="24.05"
# set all programs to C language (english)
export LC_ALL=C
# If expansion is attempted on an unset variable or parameter, the shell prints an
# error message, and, if not interactive, exits with a non-zero status.
set -o nounset
# The pipeline's return status is the value of the last (rightmost) command
# to exit with a non-zero status, or zero if all commands exit successfully.
set -o pipefail
# Enable trace mode if called with environment variable TRACE=1
if [[ "${TRACE-0}" == "1" ]]; then
set -o xtrace
fi
# shellcheck disable=SC2155
readonly PROGPATH=$(readlink -m "${0}")
# readonly PROGNAME=$(basename "${PROGPATH}")
# # shellcheck disable=SC2124
# readonly ARGS=$@
# Fetch values from evomaintenance configuration
get_evomaintenance_mail() {
grep "EVOMAINTMAIL=" /etc/evomaintenance.cf | cut -d '=' -f2
}
get_fqdn() {
hostname --fqdn
}
get_complete_hostname() {
REAL_HOSTNAME="$(get_fqdn)"
if [ "${HOSTNAME}" = "${REAL_HOSTNAME}" ]; then
echo "${HOSTNAME}"
else
echo "${HOSTNAME} (${REAL_HOSTNAME})"
fi
}
format_mail() {
cat <<EOTEMPLATE
From: Evolix <${EMAIL_FROM}>
Content-Type: text/plain; charset=UTF-8
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
X-Script: ${PROGPATH}
X-Script-Version: ${VERSION}
To: ${EMAIL_CLIENT:-alert5@evolix.fr}
Subject: Mise a jour manuelle disponible
Bonjour,
Un ou plusieurs paquets dont la mise à jour automatique a été
explicitement bloquée ont une nouvelle version disponible.
Nom du serveur :
${HOSTNAME_TEXT}
Liste des paquets :
${upgradable_held_packages}
Pour que nous appliquions ces mises à jour vous devez
nous contacter explicitement, de préférence par ticket,
en mentionnant le serveur et les paquets concernés,
ainsi que les modalités de mise à jour (créneau horaire,
procédure technique…).
Cordialement
--
Evolix
EOTEMPLATE
}
main() {
held_packages=$(apt-mark showhold)
upgradable_packages=$(apt list --upgradable 2> /dev/null)
if [ -z "${held_packages}" ]; then
# No packages are on hold
exit 0
elif [ -z "${upgradable_packages}" ]; then
# No packages are upgradable
exit 0
fi
kept_back_output=$(LC_ALL=C apt-get upgrade --dry-run | grep -A 1 'The following packages have been kept back:')
if [ -z "${kept_back_output}" ]; then
# No packages are kept back
exit 0
fi
upgradable_held_packages=$(apt list --upgradable 2> /dev/null | grep -f <(echo "${kept_back_output}" | tail -1 | tr ' ' '\n' | sed -e '/^$/d'))
if [ -z "${upgradable_held_packages}" ]; then
# No held packages are upgradable
exit 0
fi
HOSTNAME="$(get_fqdn)"
HOSTNAME_TEXT="$(get_complete_hostname)"
EMAIL_CLIENT="$(get_evomaintenance_mail)"
EMAIL_FROM="equipe@evolix.fr"
MAIL_CONTENT="$(format_mail)"
SENDMAIL_BIN="$(command -v sendmail)"
if [ -z "${SENDMAIL_BIN}" ]; then
>&2 echo "ERROR: No \`sendmail' command has been found, can't send mail."
exit 1
fi
if [ ! -x "${SENDMAIL_BIN}" ]; then
>&2 echo "ERROR: \`${SENDMAIL_BIN}' is not executable, can't send mail."
exit 1
fi
echo "${MAIL_CONTENT}" | "${SENDMAIL_BIN}" -oi -t -f "equipe@evolix.fr"
exit 0
}
main

View file

@ -1,3 +0,0 @@
Package: *
Pin: release a=stretch-backports
Pin-Priority: 50

View file

@ -10,19 +10,9 @@
tags:
- apt
- name: Backports configuration
ansible.builtin.copy:
src: '{{ ansible_distribution_release }}_backports_preferences'
dest: /etc/apt/preferences.d/0-backports-defaults
force: true
mode: "0640"
register: apt_backports_config
tags:
- apt
- name: Apt update
ansible.builtin.apt:
update_cache: yes
when: apt_backports_sources is changed or apt_backports_config is changed
when: apt_backports_sources is changed
tags:
- apt

View file

@ -17,16 +17,6 @@
tags:
- apt
- name: Backports configuration
ansible.builtin.copy:
src: '{{ ansible_distribution_release }}_backports_preferences'
dest: /etc/apt/preferences.d/0-backports-defaults
force: true
mode: "0640"
register: apt_backports_config
tags:
- apt
- name: Archived backport are accepted (jessie)
ansible.builtin.lineinfile:
dest: '/etc/apt/apt.conf.d/99no-check-valid-until'
@ -42,4 +32,4 @@
update_cache: yes
tags:
- apt
when: apt_backports_list is changed or apt_backports_config is changed
when: apt_backports_list is changed

View file

@ -76,7 +76,16 @@
tags:
- apt
- name: Check if Cron is installed
- name: List scripts is installed
ansible.builtin.copy:
src: list-upgradable-held-packages.sh
dest: /usr/share/scripts/list-upgradable-held-packages.sh
force: true
mode: "0755"
tags:
- apt
- name: Check if cron is installed
ansible.builtin.shell:
cmd: "dpkg --list 'cron' 2>/dev/null | grep -q -E '^(i|h)i'"
register: is_cron
@ -101,3 +110,15 @@
tags:
- apt
when: is_cron.rc == 0
- name: List upgradable held packages (script)
ansible.builtin.cron:
cron_file: apt-hold-packages
name: list-upgradable-held-packages
job: "/usr/share/scripts/list-upgradable-held-packages.sh"
user: root
special_time: "{{ apt_list_upgradable_held_special_time | mandatory }}"
state: "{{ apt_list_upgradable_held_enabled | bool | ternary('present', 'absent') }}"
tags:
- apt
when: is_cron.rc == 0

View file

@ -81,7 +81,7 @@
- ansible_distribution_major_version is version('12', '>=')
- name: Install Extended-LTS repositories (Debian < 10)
ansible.builtin.import_tasks: extended-lts.oneline.yml.yml
ansible.builtin.import_tasks: extended-lts.oneline.yml
tags:
- apt
when:

View file

@ -31,6 +31,11 @@
tags:
- apt
- name: Is system.sources present?
ansible.builtin.stat:
path: /etc/apt/sources.list.d/system.sources
register: _system_sources
- name: Add signed-by when relevant for bookworm
ansible.builtin.lineinfile:
dest: /etc/apt/sources.list.d/system.sources
@ -39,6 +44,12 @@
state: present
tags:
- apt
when: _system_sources.stat.exists or not ansible_check_mode
- name: Is security.sources present?
ansible.builtin.stat:
path: /etc/apt/sources.list.d/security.sources
register: _security_sources
- name: Add signed-by when relevant for bookworm-security
ansible.builtin.lineinfile:
@ -48,3 +59,4 @@
state: present
tags:
- apt
when: _security_sources.stat.exists or not ansible_check_mode

View file

@ -1,3 +1,3 @@
# {{ ansible_managed }}
deb http://mirror.evolix.org/debian buster-backports {{ apt_backports_components | mandatory }}
deb http://archive.debian.org/debian buster-backports {{ apt_backports_components | mandatory }}

View file

@ -1,5 +1,4 @@
# {{ ansible_managed }}
deb http://mirror.evolix.org/debian buster {{ apt_basics_components | mandatory }}
deb http://mirror.evolix.org/debian buster-updates {{ apt_basics_components | mandatory }}
deb http://archive.debian.org/debian buster {{ apt_basics_components | mandatory }}
deb http://security.debian.org/debian-security buster/updates {{ apt_basics_components | mandatory }}

View file

@ -0,0 +1,17 @@
---
general_scripts_dir: "/usr/share/scripts"
autosysadmin_agent_bin_dir: "/usr/local/bin/autosysadmin"
autosysadmin_agent_lib_dir: "/usr/local/lib/autosysadmin"
autosysadmin_agent_auto_dir: "{{ general_scripts_dir }}/autosysadmin/restart"
autosysadmin_agent_crontab_enabled: true
autosysadmin_agent_log_retention_days: 365
autosysadmin_config: []
### All repair are disabled if set to 'off'
### even if a specific repair value is 'on'
# repair_all: 'on'
### Default values for checks
# repair_foo: 'off'

View file

@ -0,0 +1,13 @@
/var/log/autosysadmin.log {
daily
missingok
rotate 365
compress
nodelaycompress
notifempty
dateext
dateformat .%Y-%m-%d
dateyesterday
copytruncate
create 0640 root adm
}

View file

@ -0,0 +1,3 @@
$template autosysadmin, "/var/log/autosysadmin.log"
if $programname contains 'autosysadmin' then ?autosysadmin
& stop

View file

@ -0,0 +1,25 @@
#!/bin/bash
days=${1:-365}
log_dir="/var/log/autosysadmin/"
if [ -d "${log_dir}" ]; then
find_run_dirs() {
find "${log_dir}" \
-mindepth 1 \
-maxdepth 1 \
-type d \
-ctime "+${days}" \
-print0
}
log() {
/usr/bin/logger -p local0.notice -t autosysadmin "${1}"
}
while IFS= read -r -d '' run_dir; do
rm --recursive --force "${run_dir}"
log "Delete ${run_dir} (older than ${days} days)"
done < <(find_run_dirs)
fi
exit 0

View file

@ -0,0 +1,907 @@
#!/bin/bash
VERSION="24.03.2"
# Common functions for "repair" and "restart" scripts
set -u
# Initializes the program, context, configuration…
initialize() {
PATH="${PATH}":/usr/sbin:/sbin
# Used in many places to refer to the program name.
# Examples: repair_mysql, restart_nrpe…
PROGNAME=$(basename "${0}")
# find out if running in interactive mode, or not
if [ -t 0 ]; then
INTERACTIVE=1
else
INTERACTIVE=0
fi
readonly INTERACTIVE
# Default empty value for Debug mode
DEBUG="${DEBUG:-""}"
# Repair scripts obey to the value of a variable named after the script
# You can set the value ("on" or "off") in /etc/evolinux/autosysadmin
# Here we set the default value to "on".
declare -g "${PROGNAME}"=on # dynamic variable assignment ($PROGNAME == repair_*)
PID=$$
readonly PID
# Each execution (run) gets a unique ID
RUN_ID="$(date +"%Y-%m-%d_%H-%M")_${PROGNAME}_${PID}"
readonly RUN_ID
# Main log directory
MAIN_LOG_DIR="/var/log/autosysadmin"
readonly MAIN_LOG_DIR
# shellcheck disable=SC2174
mkdir --mode=750 --parents "${MAIN_LOG_DIR}"
chgrp adm "${MAIN_LOG_DIR}"
# Each execution store some information
# in a unique directory based on the RUN_ID
RUN_LOG_DIR="${MAIN_LOG_DIR}/${RUN_ID}"
readonly RUN_LOG_DIR
# shellcheck disable=SC2174
mkdir --mode=750 --parents "${RUN_LOG_DIR}"
chgrp adm "${RUN_LOG_DIR}"
# This log file contains all events
RUN_LOG_FILE="${RUN_LOG_DIR}/autosysadmin.log"
readonly RUN_LOG_FILE
# This log file contains notable actions
ACTIONS_FILE="${RUN_LOG_DIR}/actions.log"
readonly ACTIONS_FILE
touch "${ACTIONS_FILE}"
# This log file contains abort reasons (if any)
ABORT_FILE="${RUN_LOG_DIR}/abort.log"
readonly ABORT_FILE
# touch "${ABORT_FILE}"
# Date format for log messages
DATE_FORMAT="%Y-%m-%d %H:%M:%S"
# This will contain lock, last-run markers…
# It's ok to lose the content after a reboot
RUN_DIR="/run/autosysadmin"
readonly RUN_DIR
mkdir -p "${RUN_DIR}"
# Only a singe instace of each script can run simultaneously
# We use a customizable lock name for this.
# By default it's the script's name
LOCK_NAME=${LOCK_NAME:-${PROGNAME}}
# If a lock is found, we can wait for it to disappear.
# The value must be understood by sleep(1)
LOCK_WAIT="0"
# Default values for email headers
EMAIL_FROM="equipe+autosysadmin@evolix.net"
EMAIL_INTERNAL="autosysadmin@evolix.fr"
LOCK_FILE="${RUN_DIR}/${LOCK_NAME}.lock"
readonly LOCK_FILE
# Remove lock file at exit
cleanup() {
# shellcheck disable=SC2317
rm -f "${LOCK_FILE}"
}
trap 'cleanup' 0
# Load configuration
# shellcheck disable=SC1091
test -f /etc/evolinux/autosysadmin && source /etc/evolinux/autosysadmin
log_all "Begin ${PROGNAME} RUN_ID: ${RUN_ID}"
log_all "Log directory is ${RUN_LOG_DIR}"
}
# Executes a list of tasks before exiting:
# * prepare a summary of actions and possible abort reasons
# * send emails
# * do some cleanup
quit() {
log_all "End ${PROGNAME} RUN_ID: ${RUN_ID}"
summary="RUN_ID: ${RUN_ID}"
if [ -s "${ABORT_FILE}" ]; then
# Add abort reasons to summary
summary="${summary}\n$(print_abort_reasons)"
hook_mail "abort"
return_code=1
else
if [ -s "${ACTIONS_FILE}" ]; then
# Add notable actions to summary
summary="${summary}\n$(print_actions "Aucune action")"
hook_mail "success"
fi
return_code=0
fi
hook_mail "internal"
if is_interactive; then
# shellcheck disable=SC2001
echo "${summary}" | sed -e 's/\\n/\n/g'
else
/usr/share/scripts/evomaintenance.sh --auto --user autosysadmin --message "${summary}" --no-commit --no-mail
fi
teardown
# shellcheck disable=SC2086
exit ${return_code}
}
teardown() {
:
}
# Return true/false
is_interactive() {
test "${INTERACTIVE}" -eq "1"
}
save_server_state() {
DUMP_SERVER_STATE_BIN="$(command -v dump-server-state || command -v backup-server-state)"
if [ -z "${DUMP_SERVER_STATE_BIN}" ]; then
log_all "Warning: dump-server-state is not present. No server state recorded."
fi
if [ -x "${DUMP_SERVER_STATE_BIN}" ]; then
DUMP_DIR=$(file_path_in_log_dir "server-state")
# We don't want the logging to take too much time,
# so we kill it if it takes more than 20 seconds.
timeout --signal 9 20 \
"${DUMP_SERVER_STATE_BIN}" \
--dump-dir="${DUMP_DIR}" \
--df \
--dmesg \
--iptables \
--lxc \
--netcfg \
--netstat \
--uname \
--processes \
--systemctl \
--uptime \
--virsh \
--disks \
--mysql-processes \
--no-apt-states \
--no-apt-config \
--no-dpkg-full \
--no-dpkg-status \
--no-mount \
--no-packages \
--no-sysctl \
--no-etc
log_run "Server state saved in \`server-state' directory."
fi
}
is_debug() {
# first time: do the check…
# other times: pass
if [ -z "${DEBUG:-""}" ]; then
debug_file="/etc/evolinux/autosysadmin.debug"
if [ -e "${debug_file}" ]; then
last_change=$(stat -c %Z "${debug_file}")
limit_date=$(date --date "14400 seconds ago" +"%s")
if [ $(( last_change - limit_date )) -le "0" ]; then
log_run "Debug mode disabled; file is too old (%{last_change} seconds)."
rm "${debug_file}"
# Debug mode disabled
DEBUG="0"
else
log_run "Debug mode enabled."
# Debug mode enabled
DEBUG="1"
fi
else
# log_run "Debug mode disabled; file is absent."
# Debug mode disabled
DEBUG="0"
fi
fi
# return the value
test "${DEBUG}" -eq "1"
}
# Uses the who(1) definition of "active"
currently_active_users() {
LC_ALL=C who --users | grep --extended-regexp "\s+\.\s+" | awk '{print $1}' | sort --human-numeric-sort | uniq
}
# Users active in the last 29 minutes
recently_active_users() {
LC_ALL=C who --users | grep --extended-regexp "\s+00:(0|1|2)[0-9]\s+" | awk --field-separator ' ' '{print $1,$6}'
}
# Save the list of users to a file in the log directory
save_active_users() {
LC_ALL=C who --users | save_in_log_dir "who-users"
}
# An autosysadmin must not perform actions if a user is active or was active recently.
#
# This can by bypassed in interactive mode.
# It's OK to lose this data after a reboot.
ensure_no_active_users_or_exit() {
# Save all active users
save_active_users
if is_debug; then
log_run "Debug mode enabled: continue without checking active users."
return 0;
fi
# Is there any currently active user?
currently_active_users=$(currently_active_users)
if [ -n "${currently_active_users}" ]; then
# shellcheck disable=SC2001
users_oneliner=$(echo "${currently_active_users}" | sed -e 's/\n/ /')
log_run "Currently active users: ${users_oneliner}"
if is_interactive; then
echo "Some users are currently active:"
# shellcheck disable=SC2001
echo "${currently_active_users}" | sed -e 's/\(.\+\)/* \1/'
answer=""
while :; do
printf "> Continue? [Y,n,?] "
read -r answer
case ${answer} in
[Yy]|"" )
log_run "Active users check bypassed manually in interactive mode."
return
;;
[Nn] )
log_run "Active users check confirmed manually in interactive mode."
log_abort_and_quit "Active users detected: ${users_oneliner}"
;;
* )
printf "y - yes, continue\n"
printf "n - no, exit\n"
printf "? - print this help\n"
;;
esac
done
else
log_abort_and_quit "Currently active users detected: ${users_oneliner}."
fi
else
# or recently (the last 30 minutes) active user?
recently_active_users=$(recently_active_users)
if [ -n "${recently_active_users}" ]; then
# shellcheck disable=SC2001
users_oneliner=$(echo "${recently_active_users}" | sed -e 's/\n/ /')
log_run "Recently active users: ${users_oneliner}"
if is_interactive; then
echo "Some users were recently active:"
# shellcheck disable=SC2001
echo "${recently_active_users}" | sed -e 's/\(.\+\)/* \1/'
answer=""
while :; do
printf "> Continue? [Y,n,?] "
read -r answer
case ${answer} in
[Yy]|"" )
log_run "Active users check bypassed manually in interactive mode."
return
;;
[Nn] )
log_run "Active users check confirmed manually in interactive mode."
log_abort_and_quit "Recently active users detected: ${users_oneliner}."
;;
* )
printf "y - yes, continue\n"
printf "n - no, exit\n"
printf "? - print this help\n"
;;
esac
done
else
log_abort_and_quit "Recently active users detected: ${users_oneliner}."
fi
fi
fi
}
# Takes an NRPE command name as 1st parameter,
# and executes the full command if found in the configuration.
# Return the result and the return code of the command.
check_nrpe() {
check="$1"
nrpe_files=""
# Check if NRPE config is found
if [ -f "/etc/nagios/nrpe.cfg" ]; then
nrpe_files="${nrpe_files} /etc/nagios/nrpe.cfg"
else
msg="NRPE configuration not found: /etc/nagios/nrpe.cfg"
log_run "${msg}"
echo "${msg}"
return 3
fi
# Search for included files
# shellcheck disable=SC2086
while IFS= read -r include_file; do
nrpe_files="${nrpe_files} ${include_file}"
done < <(grep --extended-regexp '^\s*include=.+' ${nrpe_files} | cut -d = -f 2)
# Search for files in included directories
# shellcheck disable=SC2086
while IFS= read -r include_dir; do
nrpe_files="${nrpe_files} ${include_dir}/*.cfg"
done < <(grep --extended-regexp '^\s*include_dir=.+' ${nrpe_files} | cut -d = -f 2)
# Fetch uncommented commands in (sorted) config files
# shellcheck disable=SC2086
nrpe_commands=$(grep --no-filename --exclude=*~ --fixed-strings "[${check}]" ${nrpe_files} | grep --invert-match --extended-regexp '^\s*#\s*command' | cut -d = -f 2)
nrpe_commands_count=$(echo "${nrpe_commands}" | wc -l)
if is_debian_version "9" "<=" && [ "${nrpe_commands_count}" -gt "1" ]; then
# On Debian <= 9, NRPE loading was not sorted
# we need to raise an error if we have multiple defined commands
msg="Unable to determine which NRPE command to run"
log_run "${msg}"
echo "${msg}"
return 3
else
# On Debian > 9, use the last command
nrpe_command=$(echo "${nrpe_commands}" | tail -n 1)
nrpe_result=$(${nrpe_command})
nrpe_rc=$?
log_run "NRPE command (exited with ${nrpe_rc}): ${nrpe_command}"
log_run "${nrpe_result}"
echo "${nrpe_result}"
return "${nrpe_rc}"
fi
}
# An autosysadmin script must not run twice (or more) simultaneously.
# We use a customizable (with LOCK_NAME) lock file to keep track of this.
# A wait time can be configured.
#
# This can by bypassed in interactive mode.
# It's OK to lose this data after a reboot.
acquire_lock_or_exit() {
lock_file="${1:-${LOCK_FILE}}"
lock_wait="${2:-${LOCK_WAIT}}"
# lock_wait must be compatible with sleep(1), otherwise fallback to 0
if ! echo "${lock_wait}" | grep -Eq '^[0-9]+[smhd]?$'; then
log_run "Lock wait: incorrect value '${lock_wait}', fallback to 0."
lock_wait=0
fi
if [ "${lock_wait}" != "0" ] && [ -f "${lock_file}" ]; then
log_run "Lock file present. Let's wait ${lock_wait} and check again."
sleep "${lock_wait}"
fi
if [ -f "${lock_file}" ]; then
log_abort_and_quit "Lock file still present."
else
log_run "Lock file absent. Let's put one."
touch "${lock_file}"
fi
}
# If a script has been run in the ast 30 minutes, running it again won't fix the issue.
# We use a /run/ausosysadmin/${PROGNAME}_lastrun file to keep track of this.
#
# This can by bypassed in interactive mode.
# This is bypassed in debug mode.
# It's OK to lose this data after a reboot.
ensure_not_too_soon_or_exit() {
if is_debug; then
log_run "Debug mode enabled: continue without checking when was the last run."
return 0;
fi
lastrun_file="${RUN_DIR}/${PROGNAME}_lastrun"
if [ -f "${lastrun_file}" ]; then
lastrun_age="$(($(date +%s)-$(stat -c "%Y" "${lastrun_file}")))"
log_run "Last run was ${lastrun_age} seconds ago."
if [ "${lastrun_age}" -lt 1800 ]; then
if is_interactive; then
echo "${PROGNAME} was run ${lastrun_age} seconds ago."
answer=""
while :; do
printf "> Continue? [Y,n,?] "
read -r answer
case ${answer} in
[Yy]|"" )
log_run "Last run check bypassed manually in interactive mode."
break
;;
[Nn] )
log_run "Last run check confirmed manually in interactive mode."
log_abort_and_quit 'Last run too recent.'
;;
* )
printf "y - yes, continue\n"
printf "n - no, exit\n"
printf "? - print this help\n"
;;
esac
done
else
log_abort_and_quit "Last run too recent."
fi
fi
fi
touch "${lastrun_file}"
}
# Populate DEBIAN_VERSION and DEBIAN_RELEASE variables
# based on gathered information about the operating system
detect_os() {
DEBIAN_RELEASE="unknown"
DEBIAN_VERSION="unknown"
LSB_RELEASE_BIN="$(command -v lsb_release)"
if [ -e /etc/debian_version ]; then
DEBIAN_VERSION="$(cut -d "." -f 1 < /etc/debian_version)"
if [ -x "${LSB_RELEASE_BIN}" ]; then
DEBIAN_RELEASE="$("${LSB_RELEASE_BIN}" --codename --short)"
else
case "${DEBIAN_VERSION}" in
7) DEBIAN_RELEASE="wheezy" ;;
8) DEBIAN_RELEASE="jessie" ;;
9) DEBIAN_RELEASE="stretch" ;;
10) DEBIAN_RELEASE="buster" ;;
11) DEBIAN_RELEASE="bullseye" ;;
12) DEBIAN_RELEASE="bookworm" ;;
13) DEBIAN_RELEASE="trixie" ;;
esac
fi
# log_run "Detected OS: Debian version=${DEBIAN_VERSION} release=${DEBIAN_RELEASE}"
# else
# log_run "Detected OS: unknown (missing /etc/debian_version)"
fi
}
is_debian_wheezy() {
test "${DEBIAN_RELEASE}" = "wheezy"
}
is_debian_jessie() {
test "${DEBIAN_RELEASE}" = "jessie"
}
is_debian_stretch() {
test "${DEBIAN_RELEASE}" = "stretch"
}
is_debian_buster() {
test "${DEBIAN_RELEASE}" = "buster"
}
is_debian_bullseye() {
test "${DEBIAN_RELEASE}" = "bullseye"
}
is_debian_bookworm() {
test "${DEBIAN_RELEASE}" = "bookworm"
}
is_debian_trixie() {
test "${DEBIAN_RELEASE}" = "trixie"
}
is_debian_version() {
local version=$1
local relation=${2:-"eq"}
if [ -z "${DEBIAN_VERSION:-""}" ]; then
detect_os
fi
dpkg --compare-versions "${DEBIAN_VERSION}" "${relation}" "${version}"
}
# List systemd services (only names), even if stopped
systemd_list_services() {
pattern=$1
systemctl list-units --all --no-legend --type=service "${pattern}" | grep --only-matching --extended-regexp '\S+\.service'
}
is_systemd_enabled() {
systemctl --quiet is-enabled "$1" 2> /dev/null
}
is_systemd_active() {
systemctl --quiet is-active "$1" 2> /dev/null
}
is_sysvinit_enabled() {
find /etc/rc2.d/ -name "$1" > /dev/null
}
get_fqdn() {
# shellcheck disable=SC2155
local system=$(uname -s)
if [ "${system}" = "Linux" ]; then
hostname --fqdn
elif [ "${system}" = "OpenBSD" ]; then
hostname
else
log_abort_and_quit "System '${system}' not recognized."
fi
}
get_complete_hostname() {
REAL_HOSTNAME="$(get_fqdn)"
if [ "${HOSTNAME}" = "${REAL_HOSTNAME}" ]; then
echo "${HOSTNAME}"
else
echo "${HOSTNAME} (${REAL_HOSTNAME})"
fi
}
# Fetch values from evomaintenance configuration
get_evomaintenance_mail() {
grep "EVOMAINTMAIL=" /etc/evomaintenance.cf | cut -d '=' -f2
}
get_evomaintenance_emergency_mail() {
grep "URGENCYFROM=" /etc/evomaintenance.cf | cut -d '=' -f2
}
get_evomaintenance_emergency_tel() {
grep "URGENCYTEL=" /etc/evomaintenance.cf | cut -d '=' -f2
}
# Log a message to the log file in the log directory
log_run() {
local msg="${1:-$(cat /dev/stdin)}"
# shellcheck disable=SC2155
local date=$(/bin/date +"${DATE_FORMAT}")
printf "[%s] %s[%s]: %s\\n" \
"${date}" "${PROGNAME}" "${PID}" "${msg}" \
>> "${RUN_LOG_FILE}"
}
# Log a message in the system log file (syslog or journald)
log_global() {
local msg="${1:-$(cat /dev/stdin)}"
echo "${msg}" \
| /usr/bin/logger -p local0.notice -t autosysadmin
}
# Log a message in both places
log_all() {
local msg="${1:-$(cat /dev/stdin)}"
log_global "${msg}"
log_run "${msg}"
}
# Log a notable action in regular places
# and append it to the dedicated list
log_action() {
log_all "$*"
append_action "$*"
}
# Append a line in the actions.log file in the log directory
append_action() {
echo "$*" >> "${ACTIONS_FILE}"
}
# Print the content of the actions.log file
# or a fallback content (1st parameter) if empty
# shellcheck disable=SC2120
print_actions() {
local fallback=${1:-""}
if [ -s "${ACTIONS_FILE}" ]; then
cat "${ACTIONS_FILE}"
elif [ -n "${fallback}" ]; then
echo "${fallback}"
fi
}
# Log a an abort reason in regular places
# and append it to the dedicated list
log_abort() {
log_all "$*"
append_abort_reason "$*"
}
# Append a line in the abort.log file in the log directory
append_abort_reason() {
echo "$*" >> "${ABORT_FILE}"
}
# Print the content of the abort.log file
# or a fallback content (1st parameter) if empty
# shellcheck disable=SC2120
print_abort_reasons() {
local fallback=${1:-""}
if [ -s "${ABORT_FILE}" ]; then
cat "${ABORT_FILE}"
elif [ -n "${fallback}" ]; then
echo "${fallback}"
fi
}
# Print the content of the main log from the log directory
print_main_log() {
cat "${RUN_LOG_FILE}"
}
# Log an abort reason and quit the script
log_abort_and_quit() {
log_abort "$*"
quit
}
# Store the content from standard inpu
# into a file in the log directory named after the 1st parameter
save_in_log_dir() {
local file_name=$1
local file_path="${RUN_LOG_DIR}/${file_name}"
cat /dev/stdin > "${file_path}"
log_run "Saved \`${file_name}' file."
}
# Return the full path of the file in log directory
# based on the name in the 1st parameter
file_path_in_log_dir() {
echo "${RUN_LOG_DIR}/${1}"
}
format_mail_success() {
cat <<EOTEMPLATE
From: AutoSysadmin Evolix <${EMAIL_FROM}>
Content-Type: text/plain; charset=UTF-8
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
X-Script: ${PROGNAME}
X-RunId: ${RUN_ID}
To: ${EMAIL_CLIENT:-alert5@evolix.fr}
Cc: ${EMAIL_INTERNAL}
Subject: [autosysadmin] Intervention automatisée sur ${HOSTNAME_TEXT}
Bonjour,
Une intervention automatisée vient de se terminer.
Nom du serveur : ${HOSTNAME_TEXT}
Heure d'intervention : $(LC_ALL=fr_FR.utf8 date)
Script déclenché : ${PROGNAME}
### Actions réalisées
$(print_actions "Aucune")
### Réagir à cette intervention
Vous pouvez répondre à ce message (${EMAIL_FROM}).
En cas d'urgence, utilisez l'adresse ${EMERGENCY_MAIL}
ou notre ligne d'astreinte (${EMERGENCY_TEL})
--
Votre AutoSysadmin
EOTEMPLATE
}
format_mail_abort() {
cat <<EOTEMPLATE
From: AutoSysadmin Evolix <${EMAIL_FROM}>
Content-Type: text/plain; charset=UTF-8
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
X-Script: ${PROGNAME}
X-RunId: ${RUN_ID}
To: ${EMAIL_CLIENT:-alert5@evolix.fr}
Cc: ${EMAIL_INTERNAL}
Subject: [autosysadmin] Intervention automatisée interrompue sur ${HOSTNAME_TEXT}
Bonjour,
Une intervention automatisée a été déclenchée mais s'est interrompue.
Nom du serveur : ${HOSTNAME_TEXT}
Heure d'intervention : $(LC_ALL=fr_FR.utf8 date)
Script déclenché : ${PROGNAME}
### Actions réalisées
$(print_actions "Aucune")
### Raison(s) de l'interruption
$(print_abort_reasons "Inconnue")
### Réagir à cette intervention
Vous pouvez répondre à ce message (${EMAIL_FROM}).
En cas d'urgence, utilisez l'adresse ${EMERGENCY_MAIL}
ou notre ligne d'astreinte (${EMERGENCY_TEL})
--
Votre AutoSysadmin
EOTEMPLATE
}
# shellcheck disable=SC2028
print_report_information() {
echo "**Uptime**"
echo ""
uptime
echo ""
echo "**Utilisateurs récents**"
echo ""
who_file=$(file_path_in_log_dir "who-users")
if [ -s "${who_file}" ]; then
cat "${who_file}"
else
who --users
fi
echo ""
echo "**Espace disque**"
echo ""
df_file=$(file_path_in_log_dir "server-state/df.txt")
if [ -s "${df_file}" ]; then
cat "${df_file}"
else
df -h
fi
echo ""
echo "**Dmesg**"
echo ""
dmesg_file=$(file_path_in_log_dir "server-state/dmesg.txt")
if [ -s "${dmesg_file}" ]; then
tail -n 5 "${dmesg_file}"
else
dmesg | tail -n 5
fi
echo ""
echo "**systemd failed services**"
echo ""
failed_services_file=$(file_path_in_log_dir "server-state/systemctl-failed-services.txt")
if [ -s "${failed_services_file}" ]; then
cat "${failed_services_file}"
else
systemctl --no-legend --state=failed --type=service
fi
if command -v lxc-ls > /dev/null 2>&1; then
echo ""
echo "**LXC containers**"
echo ""
lxc_ls_file=$(file_path_in_log_dir "server-state/lxc-list.txt")
if [ -s "${lxc_ls_file}" ]; then
cat "${lxc_ls_file}"
else
lxc-ls --fancy
fi
fi
apache_errors_file=$(file_path_in_log_dir "apache-errors.log")
if [ -f "${apache_errors_file}" ]; then
echo ""
echo "**Apache errors**"
echo ""
cat "${apache_errors_file}"
fi
nginx_errors_file=$(file_path_in_log_dir "nginx-errors.log")
if [ -f "${nginx_errors_file}" ]; then
echo ""
echo "**Nginx errors**"
echo ""
cat "${nginx_errors_file}"
fi
}
format_mail_internal() {
cat <<EOTEMPLATE
From: AutoSysadmin Evolix <${EMAIL_FROM}>
Content-Type: text/plain; charset=UTF-8
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
X-Script: ${PROGNAME}
X-RunId: ${RUN_ID}
To: ${EMAIL_INTERNAL}
Subject: [autosysadmin] Rapport interne d'intervention sur ${HOSTNAME_TEXT}
Bonjour,
Une intervention automatique vient de se terminer.
Nom du serveur : ${HOSTNAME_TEXT}
Heure d'intervention : $(LC_ALL=fr_FR.utf8 date)
Script déclenché : ${PROGNAME}
### Actions réalisées
$(print_actions "Aucune")
### Raison(s) de l'interruption
$(print_abort_reasons "Aucune")
### Log autosysadmin
$(print_main_log)
### Informations additionnelles
$(print_report_information)
--
Votre AutoSysadmin
EOTEMPLATE
}
# Generic function to send emails at the end of the script.
# Takes a template as 1st parameter
hook_mail() {
if is_debug; then
log_run "Debug mode enabled: continue without sending mail."
return 0;
fi
HOSTNAME="${HOSTNAME:-"$(get_fqdn)"}"
HOSTNAME_TEXT="$(get_complete_hostname)"
EMAIL_CLIENT="$(get_evomaintenance_mail)"
EMERGENCY_MAIL="$(get_evomaintenance_emergency_mail)"
EMERGENCY_TEL="$(get_evomaintenance_emergency_tel)"
MAIL_CONTENT="$(format_mail_"$1")"
SENDMAIL_BIN="$(command -v sendmail)"
if [ -z "${SENDMAIL_BIN}" ]; then
log_global "ERROR: No \`sendmail' command has been found, can't send mail."
fi
if [ -x "${SENDMAIL_BIN}" ]; then
echo "${MAIL_CONTENT}" | "${SENDMAIL_BIN}" -oi -t -f "equipe@evolix.fr"
log_global "Sent '$1' mail for RUN_ID: ${RUN_ID}"
fi
}
is_holiday() {
# gcal mark today as a holiday by surrounding with < and > the day
# of the month of that holiday line. For example if today is 2022-05-01 we'll
# get among other lines:
# Fête du Travail (FR) + Di, < 1>Mai 2022
# Jour de la Victoire (FR) + Di, : 8:Mai 2022 = +7 jours
LANGUAGE=fr_FR.UTF-8 TZ=Europe/Paris gcal --cc-holidays=fr --holiday-list=short | grep -E '<[0-9 ]{2}>' --quiet
}
is_weekend() {
day_of_week=$(date +%u)
if [ "${day_of_week}" != 6 ] && [ "${day_of_week}" != 7 ]; then
return 1
fi
}
is_workday() {
if is_holiday || is_weekend; then
return 1
fi
}
is_worktime() {
if ! is_workday; then
return 1
fi
hour=$(date +%H)
if [ "${hour}" -lt 9 ] || { [ "${hour}" -ge 12 ] && [ "${hour}" -lt 14 ] ; } || [ "${hour}" -ge 18 ]; then
return 1
fi
}

View file

@ -0,0 +1,112 @@
#!/bin/bash
# Specific functions for "repair" scripts
is_all_repair_disabled() {
# Fetch values from the config
# and if it is not defined or has no value, then assign "on"
local status=${repair_all:=on}
test "${status}" = "off" || test "${status}" = "0"
}
is_current_repair_disabled() {
# Fetch values from the config
# and if it is not defined or has no value, then assign "on"
local status=${!PROGNAME:=on}
test "${status}" = "off" || test "${status}" = "0"
}
ensure_not_disabled_or_exit() {
if is_all_repair_disabled; then
log_global 'All repair scripts are disabled.'
exit 0
fi
if is_current_repair_disabled; then
log_global "Current repair script (${PROGNAME}) is disabled."
exit 0
fi
}
# Set of actions to do at the begining of a "repair" script
pre_repair() {
initialize
# Are we supposed to run?
ensure_not_disabled_or_exit
# Has it recently been run?
ensure_not_too_soon_or_exit
# Can we acquire a lock?
acquire_lock_or_exit
# Is there any active user?
ensure_no_active_users_or_exit
# Save important information
save_server_state
}
# Set of actions to do at the end of a "repair" script
post_repair() {
quit
}
repair_lxc_php() {
container_name=$1
if is_systemd_enabled 'lxc.service'; then
lxc_path=$(lxc-config lxc.lxcpath)
if lxc-info --name "${container_name}" > /dev/null; then
rootfs="${lxc_path}/${container_name}/rootfs"
case "${container_name}" in
php56) fpm_log_file="${rootfs}/var/log/php5-fpm.log" ;;
php70) fpm_log_file="${rootfs}/var/log/php7.0-fpm.log" ;;
php73) fpm_log_file="${rootfs}/var/log/php7.3-fpm.log" ;;
php74) fpm_log_file="${rootfs}/var/log/php7.4-fpm.log" ;;
php80) fpm_log_file="${rootfs}/var/log/php8.0-fpm.log" ;;
php81) fpm_log_file="${rootfs}/var/log/php8.1-fpm.log" ;;
php82) fpm_log_file="${rootfs}/var/log/php8.2-fpm.log" ;;
php83) fpm_log_file="${rootfs}/var/log/php8.3-fpm.log" ;;
*)
log_abort_and_quit "Unknown container '${container_name}'"
;;
esac
# Determine FPM Pool path
php_path_pool=$(find "${lxc_path}/${container_name}/" -type d -name "pool.d")
# Save LXC info (before restart)
lxc-info --name "${container_name}" | save_in_log_dir "lxc-${container_name}.before.status"
# Save last lines of FPM log (before restart)
tail "${fpm_log_file}" | save_in_log_dir "$(basename "${fpm_log_file}" | sed -e 's/.log/.before.log/')"
# Save NRPE check (before restart)
/usr/local/lib/nagios/plugins/check_phpfpm_multi "${php_path_pool}" | save_in_log_dir "check_fpm_${container_name}.before.out"
lxc-stop --timeout 20 --name "${container_name}"
lxc-start --daemon --name "${container_name}"
rc=$?
if [ "${rc}" -eq "0" ]; then
log_action "Restart LXC container '${container_name}: OK"
else
log_action "Restart LXC container '${container_name}: failed"
fi
# Save LXC info (after restart)
lxc-info --name "${container_name}" | save_in_log_dir "lxc-${container_name}.after.status"
# Save last lines of FPM log (after restart)
tail "${fpm_log_file}" | save_in_log_dir "$(basename "${fpm_log_file}" | sed -e 's/.log/.after.log/')"
# Save NRPE check (after restart)
/usr/local/lib/nagios/plugins/check_phpfpm_multi "${php_path_pool}" | save_in_log_dir "check_fpm_${container_name}.after.out"
else
log_abort_and_quit "LXC container '${container_name}' doesn't exist."
fi
else
log_abort_and_quit 'LXC not found.'
fi
}

View file

@ -0,0 +1,76 @@
#!/bin/bash
# Specific functions for "restart" scripts
running_custom() {
# Placeholder that returns 1, to prevent running if not redefined
log_global "running_custom() function has not been redefined! Let's quit."
return 1
}
# Examine RUNNING variable and decide if the script should run or not
is_supposed_to_run() {
if is_debug; then return 0; fi
case ${RUNNING} in
never)
# log_global "is_supposed_to_run: no (never)"
return 1
;;
always)
# log_global "is_supposed_to_run: yes (always)"
return 0
;;
nwh-fr)
! is_worktime
rc=$?
# if [ ${rc} -eq 0 ]; then
# log_global "is_supposed_to_run: yes (nwh-fr returned ${rc})"
# else
# log_global "is_supposed_to_run: no (nwh-fr returned ${rc})"
# fi
return ${rc}
;;
nwh-ca)
# Not implemented yet
return 0
;;
custom)
running_custom
rc=$?
# if [ ${rc} -eq 0 ]; then
# log_global "is_supposed_to_run: yes (custom returned ${rc})"
# else
# log_global "is_supposed_to_run: no (custom returned ${rc})"
# fi
return ${rc}
;;
esac
}
ensure_supposed_to_run_or_exit() {
if ! is_supposed_to_run; then
# simply quit (no logging, no notifications…)
# log_global "${PROGNAME} is not supposed to run (RUNNING=${RUNNING})."
exit 0
fi
}
# Set of actions to do at the begining of a "restart" script
pre_restart() {
initialize
# Has it recently been run?
ensure_not_too_soon_or_exit
# Can we acquire a lock?
acquire_lock_or_exit
# Save important information
save_server_state
}
# Set of actions to do at the end of a "restart" script
post_restart() {
quit
}

View file

@ -0,0 +1,157 @@
#!/bin/bash
: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}"
source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1
source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1
pre_repair
# We always keep some reserved blocks to avoid missing some logs
# https://gitea.evolix.org/evolix/autosysadmin/issues/22
RESERVED_BLOCKS_MIN=1
get_mountpoints() {
# the $(...) get the check_disk1 command
# the cut command selects the critical part of the check_disk1 output
# the grep command extracts the mountpoints and available disk space
# the last cut command selects the mountpoints
check_disk1_command=$(grep check_disk1 /etc/nagios/nrpe.d/evolix.cfg | cut -d'=' -f2-)
${check_disk1_command} -e | cut -d'|' -f1 | grep --extended-regexp --only-matching '/[[:graph:]]* [0-9]+ [A-Z][A-Z]' | cut -d' ' -f1
}
is_reserved_blocks_nominal() {
partition=${1}
fs_type="$(findmnt -n --output=fstype "${partition}")"
if [ "${fs_type}" = "ext4" ]; then
device="$(findmnt -n --output=source "${partition}")"
reserved_block_count="$(tune2fs -l "${device}" | grep 'Reserved block count' | awk -F':' '{ gsub (" ", "", $0); print $2}')"
block_count="$(tune2fs -l "${device}" | grep 'Block count' | awk -F':' '{ gsub (" ", "", $0); print $2}')"
percentage=$(awk "BEGIN { pc=100*${reserved_block_count}/${block_count}; i=int(pc); print (pc-i<0.5)?i:i+1 }")
log_run "Reserved blocks for ${partition} is currently at ${percentage}%"
if [ "${percentage}" -gt "${RESERVED_BLOCKS_MIN}" ]; then
log_run "Allowing tune2fs action to reduce the number of reserved blocks"
return 0
else
log_run "Reserved blocks already at or bellow ${RESERVED_BLOCKS_MIN}%, no automatic action possible"
return 1
fi
else
log_run "Filesystem for ${partition} (${fs_type}) is incompatible with reserved block reduction."
return 1
fi
}
reduce_reserved_blocks() {
partition=${1}
device=$(findmnt -n --output=source "${partition}")
tune2fs -m "${RESERVED_BLOCKS_MIN}" "${device}"
log_action "Reserved blocks for ${partition} changed to ${RESERVED_BLOCKS_MIN} percent"
}
is_tmp_to_delete() {
size="$(find /var/log/ -type f -ctime +1 -exec du {} \+ | awk '{s+=$1}END{print s / 1024}')"
if [ -n "${size}" ]; then
return 0
else
return 1
fi
}
is_log_to_delete() {
size="$(find /var/log/ -type f -mtime +365 -exec du {} \+ | awk '{s+=$1}END{print s / 1024}')"
if [ -n "${size}" ]; then
return 0
else
return 1
fi
}
clean_apt_cache() {
for container in $(lxc-ls -1); do
if [ -e "$(lxc-config lxc.lxcpath)/${container}/rootfs/var/cache" ]; then
lxc-attach --name "${container}" -- apt-get clean
log_action "Clean apt cache in LXC container ${container}";
fi
done
# NOTE: "head -n 1" might be superfluous, but let's be sure to have only the first returned value
biggest_subdir=$(du --summarize --one-file-system "/var/*" | sort --numeric-sort --reverse | sed 's/^[0-9]\+[[:space:]]\+//;q' | head -n 1)
case "${biggest_subdir}" in
'/var/cache')
apt-get clean
log_action 'Clean apt cache'
;;
esac
}
clean_amavis_virusmails() {
if du --inodes /var/lib/* | sort --numeric-sort | tail -n 3 | grep --quiet 'virusmails$'; then
find /var/lib/amavis/virusmails/ -type f -atime +30 -delete
log_action 'Clean amavis infected mails'
fi
}
critical_mountpoints=$(get_mountpoints)
if [ -z "${critical_mountpoints}" ]; then
log_abort_and_quit "No partition is in critical state, nothing left to do."
else
for mountpoint in ${critical_mountpoints}; do
case "${mountpoint}" in
/var)
#if is_log_to_delete
#then
# find /var/log/ -type f -mtime +365 -delete
# log_action "$size Mo of disk space freed in /var"
#fi
if is_reserved_blocks_nominal /var; then
reduce_reserved_blocks /var
clean_apt_cache
clean_amavis_virusmails
fi
;;
/tmp)
#if is_tmp_to_delete
#then
# find /tmp/ -type f -ctime +1 -delete
# log_action "$size Mo of disk space freed in /tmp"
#fi
if is_reserved_blocks_nominal /tmp; then
reduce_reserved_blocks /tmp
fi
;;
/home)
if is_reserved_blocks_nominal /home; then
reduce_reserved_blocks /home
fi
;;
/srv)
if is_reserved_blocks_nominal /srv; then
reduce_reserved_blocks /srv
fi
;;
/filer)
if is_reserved_blocks_nominal /filer; then
reduce_reserved_blocks /filer
fi
;;
/)
if is_reserved_blocks_nominal /; then
reduce_reserved_blocks /
# Suggest remove old kernel ?
fi
;;
*)
# unknown
log_run 'Unknown partition (or weird case) or nothing to do'
;;
esac
done
fi
post_repair

View file

@ -0,0 +1,35 @@
#!/bin/bash
: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}"
source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1
source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1
pre_repair
service="elasticsearch.service"
service_name="elasticsearch"
if is_systemd_enabled "${service}"; then
if is_systemd_active "${service}"; then
log_abort_and_quit "${service} is active, nothing left to do."
else
# Save service status before restart
systemctl status "${service}" | save_in_log_dir "${service_name}.before.status"
# Try to restart
timeout 20 systemctl restart "${service}" > /dev/null
rc=$?
if [ "${rc}" -eq "0" ]; then
log_action "Restart ${service_name}: OK"
else
log_action "Restart ${service_name}: failed"
fi
# Save service status after restart
systemctl status "${service}" | save_in_log_dir "${service_name}.after.status"
fi
else
log_abort_and_quit "${service} is disabled (or missing), nothing left to do."
fi
post_repair

View file

@ -0,0 +1,131 @@
#!/bin/bash
: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}"
source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1
source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1
pre_repair
## Apache
service="apache2.service"
service_name="apache2"
if is_systemd_enabled "${service}"; then
if is_systemd_active "${service}"; then
log_all "${service} is active. Skip."
else
# Save service status before restart
systemctl status "${service}" | save_in_log_dir "${service_name}.before.status"
# check syntax
if apache2ctl -t > /dev/null 2>&1; then
# Try to restart
timeout 20 systemctl restart "${service}" > /dev/null
rc=$?
if [ "${rc}" -eq "0" ]; then
log_action "Restart ${service_name}: OK"
else
log_action "Restart ${service_name}: failed"
fi
# Save service status after restart
systemctl status "${service}" | save_in_log_dir "${service_name}.after.status"
# Save error logs
date=$(LANG=en_US.UTF-8 date '+%b %d')
grep "${date}" /home/*/log/error.log /var/log/apache2/*error.log \
| grep -v \
-e "Got error 'PHP message:" \
-e "No matching DirectoryIndex" \
-e "client denied by server configuration" \
-e "server certificate does NOT include an ID which matches the server name" \
| save_in_log_dir "apache-errors.log"
else
log_action "Restart ${service_name}: skip (invalid configuration)"
fi
fi
else
log_all "${service} is disabled (or missing). Skip."
fi
## Nginx
service="nginx.service"
service_name="nginx"
if is_systemd_enabled "${service}"; then
if is_systemd_active "${service}"; then
log_all "${service} is active. Skip."
else
# Save service status before restart
systemctl status "${service}" | save_in_log_dir "${service_name}.before.status"
# check syntax
if nginx -t > /dev/null 2>&1; then
# Try to restart
timeout 20 systemctl restart "${service}" > /dev/null
rc=$?
if [ "${rc}" -eq "0" ]; then
log_action "Restart ${service_name}: OK"
else
log_action "Restart ${service_name}: failed"
fi
# Save service status after restart
systemctl status "${service}" | save_in_log_dir "${service_name}.after.status"
# Save error logs
### Consider doing for Nginx the same as Apache
else
log_action "Restart ${service_name}: skip (invalid configuration)"
fi
fi
else
log_all "${service} is disabled (or missing). Skip."
fi
## LXC
if is_systemd_enabled 'lxc.service'; then
for container in $(lxc-ls -1 | grep --fixed-strings 'php' | grep --extended-regexp --invert-match --regexp '\bold\b' --regexp '\bdisabled\b'); do
repair_lxc_php "${container}"
done
else
log_all "LXC is disabled (or missing). Skip."
fi
## FPM
fpm_services=$(systemd_list_services 'php*-fpm*')
if [ -n "${fpm_services}" ]; then
for service in ${fpm_services}; do
service_name="${service//.service/}"
if is_systemd_enabled "${service}"; then
if is_systemd_active "${service}"; then
log_all "${service} is active. Skip."
else
# Save service status before restart
systemctl status "${service}" | save_in_log_dir "${service_name}.before.status"
# Try to restart
timeout 20 systemctl restart "${service}" > /dev/null
rc=$?
if [ "${rc}" -eq "0" ]; then
log_action "Restart ${service_name}: OK"
else
log_action "Restart ${service_name}: failed"
fi
# Save service status after restart
systemctl status "${service}" | save_in_log_dir "${service_name}.after.status"
fi
else
log_all "${service} is disabled (or missing). Skip."
fi
done
else
log_all "PHP FPM not found. Skip."
fi
post_repair

View file

@ -0,0 +1,69 @@
#!/bin/bash
: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}"
source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1
source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1
pre_repair
if is_debian_version "8" "<="; then
if is_sysvinit_enabled '*mysql*'; then
if ! pgrep -u mysql mysqld > /dev/null; then
# Save service status before restart
timeout 2 mysqladmin status 2>&1 | save_in_log_dir "mysql.before.status"
timeout 20 /etc/init.d/mysql restart > /dev/null
rc=$?
if [ "${rc}" -eq "0" ]; then
log_action "Restart mysql: OK"
else
log_action "Restart mysql: failed"
fi
# Save service status after restart
timeout 2 mysqladmin status 2>&1 | save_in_log_dir "mysql.after.status"
else
log_abort_and_quit "mysqld process alive. Aborting"
fi
else
log_abort_and_quit "MySQL not enabled. Aborting"
fi
else
if is_debian_version "12" ">="; then
service="mariadb.service"
service_name="mariadb"
else
service="mysql.service"
service_name="mysql"
fi
if is_systemd_enabled "${service}"; then
if is_systemd_active "${service}"; then
log_abort_and_quit "${service} is active, nothing left to do."
else
# Save service status before restart
systemctl status "${service}" | save_in_log_dir "${service_name}.before.status"
# Try to restart
timeout 20 systemctl restart "${service}" > /dev/null
rc=$?
if [ "${rc}" -eq "0" ]; then
log_action "Restart ${service_name}: OK"
else
log_action "Restart ${service_name}: failed"
fi
# Save service status after restart
systemctl status "${service}" | save_in_log_dir "${service_name}.after.status"
fi
else
log_abort_and_quit "${service} is disabled (or missing), nothing left to do."
fi
fi
post_repair

View file

@ -0,0 +1,35 @@
#!/bin/bash
: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}"
source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1
source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1
pre_repair
service="opendkim.service"
service_name="opendkim"
if is_systemd_enabled "${service}"; then
if is_systemd_active "${service}"; then
log_abort_and_quit "${service} is active, nothing left to do."
else
# Save service status before restart
systemctl status "${service}" | save_in_log_dir "${service_name}.before.status"
# Try to restart
timeout 20 systemctl restart "${service}" > /dev/null
rc=$?
if [ "${rc}" -eq "0" ]; then
log_action "Restart ${service_name}: OK"
else
log_action "Restart ${service_name}: failed"
fi
# Save service status after restart
systemctl status "${service}" | save_in_log_dir "${service_name}.after.status"
fi
else
log_abort_and_quit "${service} is disabled (or missing). Abort."
fi
post_repair

View file

@ -0,0 +1,14 @@
#!/bin/bash
: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}"
source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1
source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1
LOCK_WAIT="15s"
LOCK_NAME="repair_http"
pre_repair
repair_lxc_php php56
post_repair

View file

@ -0,0 +1,14 @@
#!/bin/bash
: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}"
source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1
source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1
LOCK_WAIT="15s"
LOCK_NAME="repair_http"
pre_repair
repair_lxc_php php70
post_repair

View file

@ -0,0 +1,14 @@
#!/bin/bash
: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}"
source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1
source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1
LOCK_WAIT="15s"
LOCK_NAME="repair_http"
pre_repair
repair_lxc_php php73
post_repair

View file

@ -0,0 +1,14 @@
#!/bin/bash
: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}"
source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1
source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1
LOCK_WAIT="15s"
LOCK_NAME="repair_http"
pre_repair
repair_lxc_php php74
post_repair

View file

@ -0,0 +1,14 @@
#!/bin/bash
: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}"
source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1
source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1
LOCK_WAIT="15s"
LOCK_NAME="repair_http"
pre_repair
repair_lxc_php php80
post_repair

View file

@ -0,0 +1,14 @@
#!/bin/bash
: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}"
source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1
source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1
LOCK_WAIT="15s"
LOCK_NAME="repair_http"
pre_repair
repair_lxc_php php81
post_repair

View file

@ -0,0 +1,14 @@
#!/bin/bash
: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}"
source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1
source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1
LOCK_WAIT="15s"
LOCK_NAME="repair_http"
pre_repair
repair_lxc_php php82
post_repair

View file

@ -0,0 +1,14 @@
#!/bin/bash
: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}"
source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1
source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1
LOCK_WAIT="15s"
LOCK_NAME="repair_http"
pre_repair
repair_lxc_php php83
post_repair

View file

@ -0,0 +1,32 @@
#!/bin/bash
: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}"
source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1
source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1
pre_repair
for service in $(systemd_list_services 'redis-server*'); do
service_name="${service//.service/}"
if is_systemd_active "${service}"; then
log_all "${service} is active. Skip."
else
# Save service status before restart
systemctl status "${service}" | save_in_log_dir "${service_name}.before.status"
# Try to restart
timeout 20 systemctl restart "${service}" > /dev/null
rc=$?
if [ "${rc}" -eq "0" ]; then
log_action "Restart ${service_name}: OK."
else
log_action "Restart ${service_name}: failed."
fi
# Save service status after restart
systemctl status "${service}" | save_in_log_dir "${service_name}.after.status"
fi
done
post_repair

View file

@ -0,0 +1,34 @@
#!/bin/bash
: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}"
source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1
source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1
pre_repair
repair_tomcat_instance_handle_tomcat() {
if /bin/su - "${1}" -c "/bin/systemctl --quiet --user is-active tomcat.service" ; then
if ! /bin/su - "${1}" -c "/usr/bin/timeout 20 /bin/systemctl --quiet --user restart tomcat.service"
then
log_abort_and_quit "Echec de redémarrage instance tomcat utilisateur ${1}"
else
log_action "Redémarrage instance tomcat utilisateur ${1}"
fi
elif /bin/systemctl --quiet is-active "${1}".service ; then
if ! /usr/bin/timeout 20 systemctl --quiet restart "${1}".service
then
log_abort_and_quit "Echec de redémarrage instance tomcat ${1}"
else
log_action "Redémarrage instance tomcat ${1}"
fi
fi
}
for instance in $( /usr/local/lib/nagios/plugins/check_tomcat_instance.sh |grep CRITICAL |awk '{print $3}' |sed '1d') ;
do
repair_tomcat_instance_handle_tomcat "${instance}"
done
post_repair

View file

@ -0,0 +1,41 @@
#!/bin/bash
: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}"
source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1
source "${AUTOSYSADMIN_LIB}/repair.sh" || exit 1
## Custom lock wait and/or lock name
# LOCK_WAIT="15s"
# LOCK_NAME="repair_http"
pre_repair
## The name of the service, mainly for logging
service_name="example"
## The systemd service name
systemd_service="${service_name}.service"
if is_systemd_enabled "${systemd_service}"; then
if is_systemd_active "${systemd_service}"; then
log_abort_and_quit "${systemd_service} is active, nothing left to do."
else
# Save service status before restart
systemctl status "${systemd_service}" | save_in_log_dir "${service_name}.before.status"
# Try to restart
timeout 20 systemctl restart "${systemd_service}" > /dev/null
rc=$?
if [ "${rc}" -eq "0" ]; then
log_action "Restart ${service_name}: OK"
else
log_action "Restart ${service_name}: failed"
fi
# Save service status after restart
systemctl status "${systemd_service}" | save_in_log_dir "${service_name}.after.status"
fi
else
log_abort_and_quit "${service_name} is disabled (or missing), nothing left to do."
fi
post_repair

View file

@ -0,0 +1,19 @@
Autosysadmin "restart auto" scripts
===================================
In this directory you can place scripts that will be executed automatically by a cron job (stored in `/etc/cron.d/autosysadmin`).
They must satisfy the default `run-parts(8)` constraints :
* be "executable"
* belong to the Debian cron script namespace (`^[a-zA-Z0-9_-]+$`), example: `restart_amavis`
Warning: scripts that do not satisfy those criteria will NOT be run (silently)!
You can print the names of the scripts which would be run, without actually running them, with this command :
```
$ run-parts --test /usr/share/scripts/autosysadmin/restart
```
You can use `zzz-restart_example.template` as boilerplate code to make your own "restart" script.

View file

@ -0,0 +1,120 @@
#!/bin/bash
: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}"
source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1
source "${AUTOSYSADMIN_LIB}/restart.sh" || exit 1
# shellcheck disable=SC2034
RUNNING="nwh-fr"
## Possible values for RUNNING :
## never => disabled
## always => enabled
## nwh-fr => enabled during non-working-hours in France
## nwh-ca => enabled during non-working-hours in Canada (not supported yet)
## custom => enabled if `running_custom()` function returns 0, otherwise disabled.
## Uncomment and customize this method if you want to have a special logic :
##
## return 1 if we should not run
## return 0 if we should run
##
## Some available functions :
## is_weekend() : Saturday or Sunday
## is_holiday() : holiday in France (based on `gcal(1)`)
## is_workday() : not weekend and not holiday
## is_worktime() : work day between 9-12h and 14-18h
#
# running_custom() {
# # implement your own custom method to decide if we should run or not
# }
## The name of the service, mainly for logging
service_name="example"
## The SysVinit script name
sysvinit_script="${service_name}"
## The systemd service name
systemd_service="${service_name}.service"
is_service_alive() {
## this must return 0 if the service is alive, otherwise return 1
## Example:
pgrep -u USER PROCESS_NAME > /dev/null
}
## Action for SysVinit system
sysvinit_action() {
# Save service status before restart
timeout 2 "/etc/init.d/${sysvinit_script}" status | save_in_log_dir "${service_name}.before.status"
# Try to restart
timeout 20 "/etc/init.d/${sysvinit_script}" restart > /dev/null
rc=$?
if [ "${rc}" -eq "0" ]; then
log_action "Restart ${service_name}: OK"
else
log_action "Restart ${service_name}: failed"
fi
# Save service status after restart
timeout 2 "/etc/init.d/${sysvinit_script}" status | save_in_log_dir "${service_name}.after.status"
}
## Action for systemd system
systemd_action() {
# Save service status before restart
systemctl status "${systemd_service}" | save_in_log_dir "${service_name}.before.status"
# Try to restart
# systemctl (only for NRPE ?) sometimes returns 0 even if the service has failed to start
# so we check the status explicitly
timeout 20 systemctl restart "${systemd_service}" > /dev/null \
&& sleep 1 \
&& systemctl status "${systemd_service}" > /dev/null
rc=$?
if [ "${rc}" -eq "0" ]; then
log_action "Restart ${service_name}: OK"
else
log_action "Restart ${service_name}: failed"
fi
# Save service status after restart
systemctl status "${systemd_service}" | save_in_log_dir "${service_name}.after.status"
}
# Should we run?
if ! is_supposed_to_run; then
# log_global "${PROGNAME} is not supposed to run (RUNNING=${RUNNING})."
exit 0
fi
if is_service_alive; then
# log_global "${service_name} process alive. Aborting"
exit 0
fi
# Yes we do, so check for sysvinit or systemd
if is_debian_version "8" "<="; then
if ! is_sysvinit_enabled "*${sysvinit_script}*"; then
# log_global "${service_name} not enabled. Aborting"
exit 0
fi
# Let's finally do the action
pre_restart
sysvinit_action
post_restart
else
if ! is_systemd_enabled "${systemd_service}"; then
# log_global "${service_name} is disabled (or missing), nothing left to do."
exit 0
fi
if is_systemd_active "${systemd_service}"; then
# log_global "${service_name} is active, nothing left to do."
exit 0
fi
# Let's finally do the action
pre_restart
systemd_action
post_restart
fi

View file

@ -0,0 +1,16 @@
---
- name: restart nagios-nrpe-server
service:
name: nagios-nrpe-server
state: restarted
- name: restart nrpe
service:
name: nrpe
state: restarted
- name: restart rsyslog
service:
name: rsyslog
state: restarted

View file

@ -0,0 +1,25 @@
---
- name: "Add begin marker if missing"
ansible.builtin.lineinfile:
path: "/etc/cron.d/autosysadmin"
line: "# BEGIN ANSIBLE MANAGED SECTION FOR AUTOSYSADMIN"
insertbefore: BOF
create: yes
- name: "Add end marker if missing"
ansible.builtin.lineinfile:
path: "/etc/cron.d/autosysadmin"
line: "# END ANSIBLE MANAGED SECTION FOR AUTOSYSADMIN"
insertbefore: "EOF"
create: yes
- name: "Create config if missing"
ansible.builtin.blockinfile:
path: "/etc/cron.d/autosysadmin"
marker: "# {mark} ANSIBLE MANAGED SECTION FOR AUTOSYSADMIN"
block: "{{ lookup('ansible.builtin.template', '../templates/autosysadmin.cron.j2') }}"
owner: root
group: root
mode: "0750"
create: yes

View file

@ -0,0 +1,4 @@
---
- name: Install gcal
ansible.builtin.apt:
name: gcal

View file

@ -0,0 +1,114 @@
---
- name: "Remount /usr if needed"
ansible.builtin.include_role:
name: remount-usr
- name: Previous autosysadmin restart directory is renamed
command:
cmd: mv "/usr/share/scripts/autosysadmin/auto" "{{ autosysadmin_agent_auto_dir }}"
removes: "/usr/share/scripts/autosysadmin/auto"
creates: "{{ autosysadmin_agent_auto_dir }}"
- name: Create autosysadmin directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "root"
group: "root"
mode: "0750"
loop:
- "{{ autosysadmin_agent_bin_dir }}"
- "{{ autosysadmin_agent_lib_dir }}"
- "{{ autosysadmin_agent_auto_dir }}"
- name: Copy libraries
ansible.builtin.copy:
src: "upstream/lib/"
dest: "{{ autosysadmin_agent_lib_dir }}/"
owner: root
group: root
mode: "0750"
- name: Copy repair scripts
ansible.builtin.copy:
src: "upstream/repair/"
dest: "{{ autosysadmin_agent_bin_dir }}/"
owner: root
group: root
mode: "0750"
- name: Copy other utilities
ansible.builtin.copy:
src: "upstream/bin/"
dest: "{{ autosysadmin_agent_bin_dir }}/"
owner: root
group: root
mode: "0750"
### WARNING: thos files are explicitly marked as non-executable
### to prevent them from being run automatically by run-parts
- name: Copy restart scripts
ansible.builtin.copy:
src: "upstream/restart/"
dest: "{{ autosysadmin_agent_auto_dir }}/"
owner: root
group: root
mode: "0640"
- name: Ensure /etc/evolinux folder exists
ansible.builtin.file:
path: "/etc/evolinux"
state: directory
owner: "root"
group: "root"
mode: "0700"
- name: Copy the configuration file if missing
ansible.builtin.template:
src: "autosysadmin.cf.j2"
dest: "/etc/evolinux/autosysadmin"
owner: root
group: root
mode: "0640"
force: no
# Repair scripts are supposed to be 'on' by default
# A line "repair_XXX=off" is added to the file only if the script is to be disabled.
# That's why all the ternary logic for the state is reversed.
- name: Update value per variable
ansible.builtin.lineinfile:
dest: "/etc/evolinux/autosysadmin"
line: "{{ item }}={{ autosysadmin_config[item] | default(true) | bool | ternary('on', 'off') }}"
regexp: '^(#\s*)?{{ item }}=.*'
state: "{{ autosysadmin_config[item] | default(true) | bool | ternary('absent', 'present') }}"
register: _line
loop: "{{ autosysadmin_repair_scripts | union(['repair_all']) }}"
- name: Ensure restart folder exists
ansible.builtin.file:
path: "auto"
state: directory
owner: "root"
group: "root"
mode: "0700"
- name: Legacy scripts are removed
ansible.builtin.file:
path: "{{ general_scripts_dir }}/autosysadmin/{{ item }}"
state: absent
loop:
- repair_amavis.sh
- repair_disk.sh
- repair_elasticsearch.sh
- repair_http.sh
- repair_mysql.sh
- repair_opendkim.sh
- repair_php_fpm56.sh
- repair_php_fpm70.sh
- repair_php_fpm73.sh
- repair_php_fpm74.sh
- repair_php_fpm80.sh
- repair_php_fpm81.sh
- repair_redis.sh
- repair_tomcat_instance.sh

View file

@ -0,0 +1,8 @@
---
- name: Copy logrotate configuration for autosysadmin
ansible.builtin.copy:
src: "files/autosysadmin.logrotate.conf"
dest: "/etc/logrotate.d/autosysadmin"
owner: root
group: root
mode: "0644"

View file

@ -0,0 +1,31 @@
---
- name: The list of all repair scripts is composed.
set_fact:
autosysadmin_repair_scripts: "{{ lookup('ansible.builtin.fileglob', '../../../autosysadmin/agent/repair/repair_*', wantlist=True) | map('basename') | sort }}"
- name: Install dependencies
ansible.builtin.include_tasks: dependencies.yml
- name: Install autosysadmin
ansible.builtin.include_tasks: install.yml
- name: Crontab configuration
ansible.builtin.include_tasks: crontab.yml
- name: NRPE configuration
ansible.builtin.include_tasks: nrpe.yml
- name: sudo configuration
ansible.builtin.include_tasks: sudo.yml
- name: rsyslog configuration
ansible.builtin.include_tasks: rsyslog.yml
- name: logrotate configuration
ansible.builtin.include_tasks: logrotate.yml
- name: Install latest version of dump-server-state
ansible.builtin.include_role:
name: evolinux-base
tasks_from: dump-server-state.yml

View file

@ -0,0 +1,9 @@
---
- name: custom configuration is present
ansible.builtin.template:
src: autosysadmin.nrpe.cfg.j2
dest: /etc/nagios/nrpe.d/autosysadmin.cfg
group: nagios
mode: "0640"
force: yes
notify: restart nagios-nrpe-server

View file

@ -0,0 +1,9 @@
---
- name: Copy rsyslog configuration for autosysadmin
ansible.builtin.copy:
src: "files/autosysadmin.rsyslog.conf"
dest: "/etc/rsyslog.d/autosysadmin.conf"
owner: root
group: root
mode: "0644"
notify: restart rsyslog

View file

@ -0,0 +1,7 @@
---
- name: Add autosysadmin sudoers file
ansible.builtin.template:
src: autosysadmin.sudoers.j2
dest: /etc/sudoers.d/autosysadmin
mode: "0600"
validate: "visudo -cf %s"

View file

@ -0,0 +1,12 @@
# This configuration is partially managed by Ansible
# You can change specific values manually, but they may be overridden by Ansible
#
# To be safe, update the hosts_vars/group_vars in the autosysadmin project
# https://gitea.evolix.org/evolix/autosysadmin/src/branch/master
# then use the "agent" playbook to deploy.
#
# Configuration for autosysadmin
# Use this file to change configuration values defined in repair scripts
# To disable all repair scripts : repair_all=off
# To disable "repair_http" : repair_http=off
#

View file

@ -0,0 +1,7 @@
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# Run each enabled script
*/5 * * * * root run-parts /usr/share/scripts/autosysadmin/restart
# Clean run log files
@weekly root {{ autosysadmin_agent_bin_dir | mandatory }}/delete_old_logs.sh {{ autosysadmin_agent_log_retention_days | default('365') }}

View file

@ -0,0 +1,8 @@
#
# Ansible managed - DO NOT MODIFY, your changes will be overwritten !
#
# Autosysadmin repair commands
{% for script in lookup('ansible.builtin.fileglob', '../../../autosysadmin/agent/repair/repair_*', wantlist=True) | map("basename") | sort %}
command[{{ script }}]=sudo {{ autosysadmin_agent_bin_dir }}/{{ script }}
{% endfor %}

View file

@ -0,0 +1,7 @@
#
# Ansible managed - DO NOT MODIFY, your changes will be overwritten !
#
{% for script in lookup('ansible.builtin.fileglob', '../../../autosysadmin/agent/repair/repair_*', wantlist=True) | map("basename") | sort %}
nagios ALL = NOPASSWD: {{ autosysadmin_agent_bin_dir }}/{{ script }}
{% endfor %}

View file

@ -0,0 +1,8 @@
---
general_scripts_dir: "/usr/share/scripts"
restart_nrpe_path: "{{ general_scripts_dir }}/autosysadmin/restart/restart_nrpe"
# Change this to customize the RUNNING value in the script
restart_nrpe_running: Null

View file

@ -0,0 +1,105 @@
#!/bin/bash
: "${AUTOSYSADMIN_LIB:=/usr/local/lib/autosysadmin}"
source "${AUTOSYSADMIN_LIB}/common.sh" || exit 1
source "${AUTOSYSADMIN_LIB}/restart.sh" || exit 1
## Possible values for RUNNING :
## never => disabled
## always => enabled
## nwh-fr => enabled during non-working-hours in France
## nwh-ca => enabled during non-working-hours in Canada (not supported yet)
## custom => enabled if `running_custom()` function return 0, otherwise disabled.
# shellcheck disable=SC2034
RUNNING="nwh-fr"
## The name of the service, mainly for logging
service_name="nagios-nrpe-server"
## The SysVinit script name
sysvinit_script="${service_name}"
## The systemd service name
systemd_service="${service_name}.service"
is_service_alive() {
## this must return 0 if the service is alive, otherwise return 1
## Example:
pgrep -u nagios nrpe > /dev/null
}
## Action for SysVinit system
sysvinit_action() {
# Save service status before restart
timeout 2 "/etc/init.d/${sysvinit_script}" status | save_in_log_dir "${service_name}.before.status"
# Try to restart
timeout 20 "/etc/init.d/${sysvinit_script}" restart > /dev/null
rc=$?
if [ "${rc}" -eq "0" ]; then
log_action "Restart ${service_name}: OK"
else
log_action "Restart ${service_name}: failed"
fi
# Save service status after restart
timeout 2 "/etc/init.d/${sysvinit_script}" status | save_in_log_dir "${service_name}.after.status"
}
## Action for systemd system
systemd_action() {
# Save service status before restart
systemctl status "${systemd_service}" | save_in_log_dir "${service_name}.before.status"
# Try to restart
# systemctl (only for NRPE ?) sometimes returns 0 even if the service has failed to start
# so we check the status explicitly
timeout 20 systemctl restart "${systemd_service}" > /dev/null \
&& sleep 1 \
&& systemctl status "${systemd_service}" > /dev/null
rc=$?
if [ "${rc}" -eq "0" ]; then
log_action "Restart ${service_name}: OK"
else
log_action "Restart ${service_name}: failed"
fi
# Save service status after restart
systemctl status "${systemd_service}" | save_in_log_dir "${service_name}.after.status"
}
# Should we run?
if ! is_supposed_to_run; then
# log_global "${PROGNAME} is not supposed to run (RUNNING=${RUNNING})."
exit 0
fi
if is_service_alive; then
# log_global "${service_name} process alive. Aborting"
exit 0
fi
# Yes we do, so check for sysvinit or systemd
if is_debian_version "8" "<="; then
if ! is_sysvinit_enabled "*${sysvinit_script}*"; then
# log_global "${service_name} not enabled. Aborting"
exit 0
fi
# Let's finally do the action
pre_restart
sysvinit_action
post_restart
else
if ! is_systemd_enabled "${systemd_service}"; then
# log_global "${service_name} is disabled (or missing), nothing left to do."
exit 0
fi
if is_systemd_active "${systemd_service}"; then
# log_global "${service_name} is active, nothing left to do."
exit 0
fi
# Let's finally do the action
pre_restart
systemd_action
post_restart
fi

View file

@ -0,0 +1,24 @@
---
- name: "Remount /usr if needed"
ansible.builtin.include_role:
name: remount-usr
- name: "Copy restart_nrpe"
ansible.builtin.copy:
src: upstream/restart_nrpe
dest: "{{ restart_nrpe_path }}"
owner: "root"
group: "root"
mode: "0750"
- name: "Customize RUNNING value"
ansible.builtin.lineinfile:
path: "{{ restart_nrpe_path }}"
line: "RUNNING=\"{{ restart_nrpe_running }}\""
regexp: "^ *RUNNING="
create: False
when:
- restart_nrpe_running is defined
- restart_nrpe_running != None
- restart_nrpe_running | length > 0

37
bind/files/bind-reload-zone.sh Executable file
View file

@ -0,0 +1,37 @@
#!/bin/bash
#
# Script utilitaire pour tester et recharger facilement une zone dans Bind
#
usage() {
echo "Usage: bind-reload-zone <DOMAIN>"
echo " bind-reload-zone -h|--help"
}
if [ $# -ne 1 ] ; then
usage
exit 1
fi
while :; do
case $1 in
-h|--help)
usage
exit 0
;;
*)
zone=$1
break
;;
esac
shift
done
if ! [ -f "/etc/bind/db.${zone}" ]; then
>&2 echo "Error: zone for ${zone} not found."
usage
exit 1
fi
named-checkzone "${zone}" /etc/bind/db."${zone}" && rndc reload "${zone}"

View file

@ -0,0 +1,18 @@
#!/usr/bin/env bash
_bind_reload_zone_dynamic_completion() {
local cur;
cur=${COMP_WORDS[COMP_CWORD]};
COMPREPLY=();
COMPREPLY=( $( compgen -W '$(grep -v -h '"'"'//'"'"' /etc/bind/named.conf* | grep -B1 "type master" | grep zone | grep -v arpa | awk '"'"'{gsub(/"/, "", $2); print $2}'"'"' | sort | uniq)' -- $cur ) );
# reverse ipv4 :
#grep -v -h '//' /etc/bind/named.conf* | grep -B1 "type master" | grep zone | grep arpa | grep -v ip6 | awk '{gsub(/"/, "", $2); gsub(/.in-addr.arpa/, "", $2); print $2}' | sort | uniq | awk -F'.' '{ for (i=NF; i>1; i--) printf("%s.",$i); print $1 }'
# reveres ipv6 : je bloque sur l'inversion 4 par 4
#grep -v -h '//' /etc/bind/named.conf* | grep -B1 "type master" | grep zone | grep arpa | grep ip6 | awk '{gsub(/"/, "", $2); gsub(/.ip6.arpa/, "", $2); print $2}' | sort | uniq | awk -F'.' '{ for (i=NF; i>1; i--) { if ($i % 4 == 0) printf("%s.",$i); else printf("%s",$i); } print $1 }'
}
complete -F _bind_reload_zone_dynamic_completion bind-reload-zone

View file

@ -1,14 +0,0 @@
#!/bin/bash
#
# Script utilitaire pour tester et recharger facilement un domaine dans Bind
# Usage : reload-zone <DOMAINE>
#
# TODO:
# - renommer le script (par ex bind-safe-reload)
# - vérifier le serial
# - ajouter un -h --help
# - prendre en charge plusieurs zones (ou aucune)
# - ajouter le script dans le role bind
named-checkzone "$1" /etc/bind/db."$1" && rndc reload "$1"

View file

@ -39,8 +39,8 @@ concat_files() {
chown root: "${hapee_cert_file}"
}
cert_and_key_mismatch() {
hapee_cert_md5=$(openssl x509 -noout -modulus -in "${hapee_cert_file}" | openssl md5)
hapee_key_md5=$(openssl rsa -noout -modulus -in "${hapee_cert_file}" | openssl md5)
hapee_cert_md5=$(openssl x509 -noout -pubkey -in "${hapee_cert_file}" | openssl md5)
hapee_key_md5=$(openssl pkey -pubout -in "${hapee_cert_file}" | openssl md5)
test "${hapee_cert_md5}" != "${hapee_key_md5}"
}

View file

@ -29,8 +29,8 @@ concat_files() {
chown root: "${haproxy_cert_file}"
}
cert_and_key_mismatch() {
haproxy_cert_md5=$(openssl x509 -noout -modulus -in "${haproxy_cert_file}" | openssl md5)
haproxy_key_md5=$(openssl rsa -noout -modulus -in "${haproxy_cert_file}" | openssl md5)
haproxy_cert_md5=$(openssl x509 -noout -pubkey -in "${haproxy_cert_file}" | openssl md5)
haproxy_key_md5=$(openssl pkey -pubout -in "${haproxy_cert_file}" | openssl md5)
test "${haproxy_cert_md5}" != "${haproxy_key_md5}"
}

View file

@ -0,0 +1,44 @@
#!/bin/sh
error() {
>&2 echo "${PROGNAME}: $1"
exit 1
}
debug() {
if [ "${VERBOSE}" = "1" ] && [ "${QUIET}" != "1" ]; then
>&2 echo "${PROGNAME}: $1"
fi
}
daemon_found_and_running() {
test -n "$(pidof nrpe)"
}
letsencrypt_lineaged_used() {
grep -r "^ssl_cert_file" /etc/nagios/ | grep "letsencrypt" | grep -q "$(basename "${RENEWED_LINEAGE}")"
}
copy_letsencrypt_cert() {
DEST_CERTIFICATE=$(grep -r "^ssl_cert_file" /etc/nagios/ | awk -F'=' '{print $2}')
DEST_PRIVATE_KEY=$(grep -r "^ssl_privatekey_file" /etc/nagios/ | awk -F'=' '{print $2}')
install --mode 440 --group nagios ${RENEWED_LINEAGE}/fullchain.pem ${DEST_CERTIFICATE}
install --mode 440 --group nagios ${RENEWED_LINEAGE}/privkey.pem ${DEST_PRIVATE_KEY}
}
main() {
if daemon_found_and_running; then
if letsencrypt_lineaged_used; then
debug "NRPE detected... Copying certificates to the right place & permissions"
copy_letsencrypt_cert
debug "Restarting NRPE"
systemctl restart nagios-nrpe-server
else
debug "NRPE doesn't use the given Let's Encrypt certificate. Skip."
fi
else
debug "NRPE is not running or missing. Skip."
fi
}
readonly PROGNAME=$(basename "$0")
readonly VERBOSE=${VERBOSE:-"0"}
readonly QUIET=${QUIET:-"0"}
main

View file

@ -21,6 +21,8 @@
- ansible.builtin.include: acme-challenge.yml
# This is always going to mark a "change".
# Couldn't figure out why !
- name: Deploy hooks are present
ansible.builtin.copy:
src: hooks/deploy/

View file

@ -0,0 +1,6 @@
---
check_free_space_partitions:
- "/home"
- "/srv"
check_free_space_max_percent: 70
check_free_space_mailto: Null

View file

@ -0,0 +1,166 @@
#!/bin/sh
# This script verifies if the specified partitions on a machine are filled
# at more than x%.
#
# If so, it sends a mail to the admin of that machine, warning him/her
# that mesures should be taken.
#
# Two outputs are provided to the recipient of the mail:
# * some general infos with `df`
# * a more indepth inspection with `duc`
#
# This script takes 3 (mandatory) arguments:
# * a list of the partitions to check (space separated)
# * the maximum allowed percentage
# * the email template to use
#
# This script should be ran by cron @daily.
#
#
# Copyright (C) 2016 Louis-Philippe Véronneau <lpveronneau@evolix.ca, Evolix <info@evolix.fr>
#
# This program is licensed under GPLv3 +
# Check argument sanity
PID_FILE='/var/run/check_free_space.pid'
if test -f "$PID_FILE"
then
pid=$(cat "$PID_FILE")
ps -p "$pid" > /dev/null
if test $? -eq 0
then
echo "$0 already run !" >&2
exit 1
else
rm $PID_FILE
fi
fi
echo $$ > $PID_FILE
if test -z "$1" || test -z "$2" || test -z "$3" # is non null
then
echo "Some arguments are missing. Please issue a partition list, a" \
"maximum percentage and an email template."
exit 1
elif ! [ "$2" -le 100 -a "$2" -ge 0 ] # is a percentage
then
echo "Please enter a maximum percentage value between 0 and 100."
exit 1
fi
# Argument processing
partition_list=$1
max_percentage=$((100-$2))
email_template=$3
HOSTNAME=$(hostname)
debian_version=$(lsb_release -c)
check_disk='/usr/lib/nagios/plugins/check_disk'
test -f /etc/evomaintenance.cf && . /etc/evomaintenance.cf
# Test what version of df we have
old_df=false
case "$debian_version" in
*squeeze* ) old_df=true ;;
*wheezy* ) old_df=true ;;
esac
# Check disk space
df_options="size,avail,pcent,itotal,iavail,ipcent"
for partition in $partition_list
do
if ! $check_disk -w $max_percentage% -W $max_percentage% $partition > /dev/null
then
# the 'newline' is a hack to make sed behave
PARTITION_DATA="$PARTITION_DATA newline $partition newline"
if [ $old_df ]
then
PARTITION_DATA="$PARTITION_DATA $(/bin/df -h $partition) newline"
PARTITION_DATA="$PARTITION_DATA newline $(df -ih $partition) newlinenewline"
else
PARTITION_DATA="$PARTITION_DATA $(/bin/df -h --output=$df_options $partition) newline"
fi
full_partitions="$full_partitions $partition"
partname=$(echo $partition|tr -s '/' '-')
graph_list="$graph_list -a /home/duc${partname}.png"
fi
done
# Exit if everything is OK
if test -z "$PARTITION_DATA"
then
exit 0
fi
# If there is indeed a problem, get more infos with duc
/usr/bin/ionice -c3 /usr/bin/duc index -H -d /home/duc.idx -x $full_partitions -q
for partition in $full_partitions
do
duc_temp=$(/usr/bin/duc ls -d /home/duc.idx -Fg $partition)
duc_temp=$(printf "$duc_temp" | sed -e "s@]@]newline@" | grep -v "lost+found")
DUC_OUTPUT="$DUC_OUTPUT newline$partition newline$duc_temp"
partname=$(echo $partition|tr -s '/' '-')
duc graph -d /home/duc.idx -o /home/duc${partname}.png -l8 -s 1024 $partition
done
# Replace placeholders & send the mail !
PARTITION_DATA="$(echo "$PARTITION_DATA"|tr -d $'\n')" # make sed accept the input
DUC_OUTPUT="$(echo "$DUC_OUTPUT"|tr -d $'\n')"
if [ $old_df ]
then
sed -e "s/__TO__/$EVOMAINTMAIL/" \
-e "s/__HOSTNAME__/$HOSTNAME/" \
-e "s@__PARTITION_DATA__@$PARTITION_DATA@" \
-e "s@__DUC_OUTPUT__@$DUC_OUTPUT@" \
-e "s/newline/\n/g" \
-e "s/IUse%/IUse%\n/g" \
-e "s/ Use%/ Use%\n/g" \
-e "s@Filesystem \{12\}@@g" \
-e "s@Mounted on\/dev\/[a-z]\{3\}[0-9]\+ \{13\}@@g" \
-e "s@% \/[a-z]\+@%@g" \
-e "s/__MAX_PERCENTAGE__/$max_percentage/" \
-e "s/__FULLFROM__/$FULLFROM/" \
-e "s/__FROM__/$FROM/" \
-e "s/__URGENCYFROM__/$URGENCYFROM/" \
-e "s/__URGENCYTEL__/$URGENCYTEL/" \
$email_template | \
/usr/bin/mutt -H - $graph_list
else
sed -e "s/__TO__/$EVOMAINTMAIL/" \
-e "s/__HOSTNAME__/$HOSTNAME/" \
-e "s@__PARTITION_DATA__@$PARTITION_DATA@" \
-e "s@__DUC_OUTPUT__@$DUC_OUTPUT@" \
-e "s/newline/\n/g" \
-e "s/IUse%/IUse%\n/g" \
-e "s/__MAX_PERCENTAGE__/$max_percentage/" \
-e "s/__FULLFROM__/$FULLFROM/" \
-e "s/__FROM__/$FROM/" \
-e "s/__URGENCYFROM__/$URGENCYFROM/" \
-e "s/__URGENCYTEL__/$URGENCYTEL/" \
$email_template | \
/usr/bin/mutt -H - $graph_list
fi
rm -f $PID_FILE

View file

@ -0,0 +1,24 @@
From: __FULLFROM__
Content-Type: text/plain; charset=UTF-8
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
To: __TO__
Subject: [WARNING] Espace disque faible sur __HOSTNAME__
Bonjour,
Ceci est un message automatique pour vous informer qu'il y a un
souci d'espace disque sur votre serveur __HOSTNAME__
Voici les informations sur l'espace disque qui pose problème :
__PARTITION_DATA__
Détails sur les partitions problématiques :
__DUC_OUTPUT__
Un graphe par partition problématique est disponible en pièce jointe.
Nous vous recommandons d'effectuer du ménage pour maintenir
chaque partition avec un minimum de __MAX_PERCENTAGE__% d'espace disque libre.
Cordialement,
--
__FULLFROM__

View file

@ -0,0 +1,37 @@
---
- ansible.builtin.include_role:
name: evolix/remount-usr
- name: Copy check_free_space.sh script
ansible.builtin.copy:
src: files/check_free_space.sh
dest: /usr/share/scripts/check_free_space
owner: root
group: root
mode: "0750"
- name: Copy email template
ansible.builtin.copy:
src: files/check_free_space.tpl
dest: /usr/share/scripts/check_free_space.tpl
owner: root
group: root
mode: "0644"
# not using the cron_module for this since it is buggy
- name: check_free_space.sh is run by cron
ansible.builtin.template:
src: templates/cron_check_free_space.j2
dest: /etc/cron.d/check_free_space
owner: root
group: root
mode: "0644"
force: false
- name: Duc and Mutt are installed
ansible.builtin.apt:
pkg:
- mutt
- duc
state: present

View file

@ -0,0 +1,30 @@
---
- include_role:
name: evolix/remount-usr
- name: shell script
copy:
src: files/check_free_space.sh
dest: /usr/share/scripts/check_free_space
owner: root
group: root
mode: "0750"
- name: email template
copy:
src: files/check_free_space.tpl
dest: /usr/share/scripts/check_free_space.tpl
owner: root
group: root
mode: "0644"
# not using the cron_module for this since it is buggy
- name: cron
template:
src: templates/cron_check_free_space.j2
dest: /etc/cron.d/check_free_space
owner: root
group: root
mode: "0644"
force: false

View file

@ -0,0 +1,4 @@
{% if check_free_space_mailto and check_free_space_mailto != "" %}
MAILTO={{ check_free_space_mailto }}
{% endif %}
30 4 * * 1 root /usr/share/scripts/check_free_space "{{ check_free_space_partitions | join(' ') }}" {{ check_free_space_max_percent }} /usr/share/scripts/check_free_space.tpl

View file

@ -3,28 +3,25 @@
docker_home: /var/lib/docker
docker_tmpdir: "{{ docker_home }}/tmp"
# Chose to use iptables instead of docker-proxy userland process
docker_conf_use_iptables: False
# Disable the possibility for containers processes to gain new privileges
docker_conf_no_newprivileges: False
docker_conf_no_newprivileges: false
# Toggle live restore (need to be disabled in swarm mode)
docker_conf_live_restore: True
docker_conf_live_restore: true
# Toggle user namespace
docker_conf_user_namespace: True
docker_conf_user_namespace: true
# Disable all default network connectivity
docker_conf_disable_default_networking: False
docker_conf_disable_default_networking: false
# Remote access
docker_remote_access_enabled: False
docker_remote_access_enabled: false
docker_daemon_port: 2376
docker_daemon_listening_ip: 0.0.0.0
# TLS
docker_tls_enabled: False
docker_tls_enabled: false
docker_tls_path: "{{ docker_home }}/tls"
docker_tls_ca: ca/ca.pem
docker_tls_ca_key: ca/ca-key.pem
@ -32,4 +29,4 @@ docker_tls_cert: server/cert.pem
docker_tls_key: server/key.pem
docker_tls_csr: server/server.csr
apt_keyring_dir: "{{ ansible_distribution_major_version is version('12', '<') | ternary('/etc/apt/trusted.gpg.d', '/etc/apt/keyrings') }}"
apt_keyring_dir: "{{ ansible_distribution_major_version is version('12', '<') | ternary('/etc/apt/trusted.gpg.d', '/etc/apt/keyrings') }}"

View file

@ -1,5 +1,14 @@
# This role installs the docker daemon
---
- name: Fail if docker_conf_use_iptables is defined
ansible.builtin.fail:
msg: "Variable docker_conf_use_iptables is deprecated and not configurable anymore. Please remove it from your variables. Also double-check the daemon.json config for docker"
when:
- docker_conf_use_iptables is defined
tags:
- always
- name: Remove older docker packages
ansible.builtin.apt:
name:
@ -23,7 +32,7 @@
when: ansible_distribution_major_version is version('10', '<')
- name: "Ensure {{ apt_keyring_dir }} directory exists"
file:
ansible.builtin.file:
path: "{{ apt_keyring_dir }}"
state: directory
mode: "755"
@ -44,35 +53,34 @@
repo: 'deb [signed-by={{ apt_keyring_dir }}/docker-debian.asc] https://download.docker.com/linux/debian {{ ansible_distribution_release }} stable'
filename: docker
state: present
update_cache: yes
update_cache: true
when: ansible_distribution_major_version is version('12', '<')
- name: Add Docker repository (Debian >=12)
ansible.builtin.template:
src: docker.sources.j2
dest: /etc/apt/sources.list.d/docker.sources
register: docker_sources
owner: root
group: root
mode: "0644"
when: ansible_distribution_major_version is version('12', '>=')
- name: Update APT cache
ansible.builtin.apt:
update_cache: yes
when: docker_sources is changed
- name: Install Docker
ansible.builtin.apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
update_cache: true
cache_valid_time: 3600
- name: python-docker is installed
- name: Package python-docker is installed
ansible.builtin.apt:
name: python-docker
state: present
when: ansible_python_version is version('3', '<')
- name: python3-docker is installed
- name: Package python3-docker is installed
ansible.builtin.apt:
name: python3-docker
state: present
@ -82,6 +90,9 @@
ansible.builtin.template:
src: daemon.json.j2
dest: /etc/docker/daemon.json
owner: root
group: root
mode: "0644"
notify: restart docker
- name: Creating Docker tmp directory

View file

@ -1,5 +1,6 @@
{
"debug": false
,"iptables": true
{# Docker data-dir (default to /var/lib/docker) #}
,"data-root": "{{ docker_home }}"
{# Keep containers running while docker daemon downtime #}
@ -7,11 +8,6 @@
{% if docker_conf_user_namespace %}
{# Turn on user namespace remaping #}
,"userns-remap": "default"
{% endif %}
{% if docker_conf_use_iptables %}
{# Use iptables instead of docker-proxy #}
,"userland-proxy": false
,"iptables": true
{% endif %}
{# Disable the possibility for containers processes to gain new privileges #}
,"no-new-privileges": {{ docker_conf_no_newprivileges | to_json }}

View file

@ -1,4 +1,12 @@
---
general_alert_email: "root@localhost"
log2mail_alert_email: Null
dovecot_vmail_uid: 5000
dovecot_vmail_gid: 5000
ldap_hostname: "{{ ansible_hostname }}"
ldap_domain: "{{ ansible_domain }}"
ldap_suffix: "dc={{ ldap_hostname }},dc={{ ldap_domain.split('.')[-2] }},dc={{ ldap_domain.split('.')[-1] }}"
ldap_enabled: False

24
dovecot/files/munin_plugin_dovecot1 Normal file → Executable file
View file

@ -53,15 +53,17 @@ if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) {
exit 0;
}
if (-f "$logfile.0") {
$rotlogfile = $logfile . ".0";
} elsif (-f "$logfile.1") {
$rotlogfile = $logfile . ".1";
} elsif (-f "$logfile.01") {
$rotlogfile = $logfile . ".01";
} else {
$rotlogfile = $logfile . ".0";
}
# Disable rotated log inpection because name is not deterministic across systems
# and data loss is may 5 min
#if (-f "$logfile.0") {
# $rotlogfile = $logfile . ".0";
#} elsif (-f "$logfile.1") {
# $rotlogfile = $logfile . ".1";
#} elsif (-f "$logfile.01") {
# $rotlogfile = $logfile . ".01";
#} else {
# $rotlogfile = $logfile . ".0";
#}
if ( $ARGV[0] and $ARGV[0] eq "config" ) {
print "multigraph dovecot_connections\n";
@ -179,7 +181,9 @@ if (!defined $pos) {
if ($startsize < $pos) {
# Log rotated
parseDovecotfile ($rotlogfile, $pos, (stat $rotlogfile)[7]);
# Disable rotated log inpection because name is not deterministic across systems
# and data loss is may 5 min
#parseDovecotfile ($rotlogfile, $pos, (stat $rotlogfile)[7]);
$pos = 0;
}

View file

@ -1,3 +1,5 @@
---
- name: ensure packages are installed
ansible.builtin.apt:
name:
@ -8,7 +10,7 @@
- dovecot-managesieved
state: present
tags:
- dovecot
- dovecot
- name: Generate 4096 bits Diffie-Hellman parameters (may take several minutes)
community.crypto.openssl_dhparam:
@ -21,7 +23,7 @@
regexp: "[^#]!include auth-system.conf.ext"
replace: "#!include auth-system.conf.ext"
tags:
- dovecot
- dovecot
- name: update ldap auth
ansible.builtin.lineinfile:
@ -33,14 +35,15 @@
- { key: 'hosts', value: '127.0.0.1' }
- { key: 'auth_bind', value: 'yes' }
- { key: 'ldap_version', value: 3 }
- { key: 'base', value: "{{ ldap_suffix }}" }
- { key: 'base', value: "{{ ldap_suffix | mandatory }}" }
- { key: 'user_attrs', value: 'homeDirectory=home' }
- { key: 'user_filter', value: '(&(isActive=TRUE)(uid=%u))' }
- { key: 'pass_attrs', value: 'uid=user,userPassword=password' }
when: ldap_suffix is defined
- { key: 'iterate_filter', value: '(&(isActive=TRUE))' }
when: ldap_enabled | bool | default(False)
notify: reload dovecot
tags:
- dovecot
- dovecot
- name: create vmail group
ansible.builtin.group:
@ -48,7 +51,7 @@
gid: "{{ dovecot_vmail_gid }}"
system: True
tags:
- dovecot
- dovecot
- name: create vmail user
ansible.builtin.user:
@ -58,16 +61,16 @@
shell: /bin/false
system: True
tags:
- dovecot
- dovecot
- name: deploy evolix config
- name: deploy evolix config for Dovecot
ansible.builtin.template:
src: z-evolinux-defaults.conf.j2
dest: /etc/dovecot/conf.d/z-evolinux-defaults.conf
mode: "0644"
notify: reload dovecot
tags:
- dovecot
- dovecot
- name: deploy file for custom configuration
ansible.builtin.template:
@ -76,7 +79,7 @@
mode: "0644"
notify: reload dovecot
tags:
- dovecot
- dovecot
- ansible.builtin.include: munin.yml
tags:
@ -86,7 +89,8 @@
ansible.builtin.apt:
name: log2mail
state: present
tags: dovecot
tags:
- dovecot
- name: dovecot is configured in log2mail
ansible.builtin.blockinfile:
@ -101,5 +105,6 @@
mailto = {{ log2mail_alert_email or general_alert_email | mandatory }}
template = /etc/log2mail/mail
notify: restart log2mail
tags: dovecot
tags:
- dovecot

View file

@ -9,20 +9,18 @@
- name: Munin plugins are present and configured
block:
- name: Disable dovecot plugin
- name: Disable Dovecot plugin
ansible.builtin.file:
path: /etc/munin/plugins/dovecot
state: absent
- name: Remove old dovecot plugin conf
- name: Remove old Dovecot plugin conf
ansible.builtin.file:
path: /etc/munin/plugin-conf.d/dovecot
state: absent
- name: Remove old dovecot plugin conf
ansible.builtin.file:
path: /etc/munin/plugin-conf.d/z-evolinux-dovecot
path: "/etc/munin/plugin-conf.d/{{ item }}"
state: absent
loop:
- dovecot
- z-evolinux-dovecot
- name: "Remount /usr if needed"
ansible.builtin.include_role:
@ -51,7 +49,7 @@
- name: Copy Munin config
ansible.builtin.copy:
src: z-evolinux-dovecot.conf
src: munin_plugins.conf
dest: /etc/munin/plugin-conf.d/zzz-dovecot
mode: '0644'
notify: restart munin-node

View file

@ -1,23 +1,16 @@
# evobackup-client
Allows the configuration of backups to one or more remote filesystems.
Install the necessary libraries and script to configure backup scripts.
The backup hosts and the ports in use need to be defined in
evobackup-client__hosts before running it.
Additional information:
The default zzz_evobackup.sh configures a system only backup, but the
template can be overriden to configure a full backup instead. If
you change the variables in defaults/main.yml you can easily run
this again and configure backups to a second set of hosts.
* [evobackup-client documentation](https://gitea.evolix.org/evolix/evobackup/src/branch/master/client/README.md)
* canary
Do not forget to set the evobackup-client__mail variable to an
email adress you control.
## Available variables
You can add this example to an installation playbook to create the
ssh key without running the rest of the role.
~~~
post_tasks:
- include_role:
name: evobackup-client tasks_from: ssh_key.yml
~~~
* `evobackup_client__lib_dir` : directory for libraries (default: `/usr/local/lib/evobackup`)
* `evobackup_client__bin_dir` : directory for scripts/binaries (default: `/usr/local/bin`)
* `evobackup_client__update_canary_enable` : should the canary be updated (default: `True`)
* `evobackup_client__update_canary_path` : path for the canary update script (default: `/etc/cron.daily/000-update-evobackup-canary`)
* `evobackup_client__update_canary_who` : who the canary update must be attributed to (default: `@daily`)

View file

@ -1,15 +1,22 @@
---
evobackup_client__root_key_path: "/root/.ssh/id_ed25519"
evobackup_client__root_key_type: "ed25519"
evobackup_client__cron_path: "/etc/cron.daily/zzz_evobackup"
evobackup_client__cron_template_name: "zzz_evobackup"
evobackup_client__mail: null
evobackup_client__servers_fallback: -1
evobackup_client__pid_path: "/var/run/evobackup.pid"
evobackup_client__log_path: "/var/log/evobackup.log"
evobackup_client__backup_path: "/home/backup"
evobackup_client__hosts: null
# evobackup_client__root_key_path: "/root/.ssh/id_ed25519"
# evobackup_client__root_key_type: "ed25519"
# evobackup_client__cron_path: "/etc/cron.daily/zzz_evobackup"
# evobackup_client__cron_template_name: "zzz_evobackup"
# evobackup_client__mail: null
# evobackup_client__servers_fallback: -1
# evobackup_client__pid_path: "/var/run/evobackup.pid"
# evobackup_client__log_path: "/var/log/evobackup.log"
# evobackup_client__backup_path: "/home/backup"
# evobackup_client__hosts: null
# - name: "backups.example.org"
# ip: "xxx.xxx.xxx.xxx"
# fingerprint: "ecdsa-sha2-nistp256 ..."
# port: xxxx
evobackup_client__lib_dir: "/usr/local/lib/evobackup"
evobackup_client__bin_dir: "/usr/local/bin"
evobackup_client__update_canary_enable: True
evobackup_client__update_canary_path: /etc/cron.daily/000-update-evobackup-canary
evobackup_client__update_canary_who: "@daily"

View file

@ -0,0 +1,98 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
This project does not follow semantic versioning.
The **major** part of the version is the year
The **minor** part changes is the month
The **patch** part changes is incremented if multiple releases happen the same month
## [Unreleased]
### Added
### Changed
### Deprecated
### Removed
### Fixed
### Security
## [24.05.1] - 2022-05-14
### Fixed
* client: fix shell syntax error
## [24.05] - 2022-05-02
### Added
* evobackupctl: update LIBDIR when copying the template
### Changed
* evobackupctl: simplify the program path retrieval
## [24.04.1] - 2022-04-30
### Fixed
* evobackupctl: quote ARGS variable for options parsing.
## [24.04] - 2022-04-29
### Added
* Vagrant definition for manual tests
### Changed
* split functions into libraries
* add evobackupctl script
* change the "zzz_evobackup" script to a template, easy to copy with evobackupctl
* use env-based shebang for shell scripts
* use $TMPDIR if available
### Removed
* update-evobackup-canary is managed by ansible-roles.git
* deployment by Ansible is managed elsewhere (now in evolix-private.git, later in ansible-roles.git)
### Fixed
* don't exit the whole program if a sync task can't be done
## [22.12] - 2022-12-27
### Changed
* Use --dump-dir instead of --backup-dir to suppress dump-server-state warning
* Do not use rsync compression
* Replace rsync option --verbose by --itemize-changes
* Add canary to zzz_evobackup
* update-evobackup-canary: do not use GNU date, for it to be compatible with OpenBSD
* Add AGPL License and README
* Script now depends on Bash
* tolerate absence of mtr or traceroute
* Only one loop for all Redis instances
* remodel how we build the rsync command
* use sub shells instead of moving around
* Separate Rsync for the canary file if the main Rsync has finished without errors
### Removed
* No more fallback if dump-server-state is missing
### Fixed
* Make start_time and stop_time compatible with OpenBSD
## [22.03] - 2022-04-03
Split client and server parts of the project

View file

@ -0,0 +1,156 @@
#!/usr/bin/env bash
# shellcheck disable=SC2155
readonly PROGPATH=$(readlink -m "${0}")
readonly PROGNAME=$(basename "${PROGPATH}")
# shellcheck disable=SC2124
readonly ARGS=$@
# Change this to wherever you install the libraries
readonly LIBDIR="/usr/local/lib/evobackup"
source "${LIBDIR}/main.sh"
show_version() {
cat <<END
${PROGNAME} version ${VERSION}
Copyright 2024 Evolix <info@evolix.fr>,
Jérémy Lecour <jlecour@evolix.fr>.
${PROGNAME} comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to redistribute it under certain conditions.
See the GNU General Public License v3.0 for details.
END
}
show_help() {
cat <<END
${PROGNAME} helps managing evobackup scripts
Options
-h, --help print this message and exit
-V, --version print version and exit
--jail-init-commands print jail init commands
--copy-template=PATH copy the backup template to PATH
END
}
jail_init_commands() {
if [ ! -f /root/.ssh/id_ed25519.pub ]; then
ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -N ''
echo ""
fi
SSH_KEY=$(cat /root/.ssh/id_ed25519.pub)
SERVER_NAME=$(hostname -s)
if [ "$(uname -s)" = "OpenBSD" ]; then
SERVER_IP=$(ifconfig egress | grep "inet " | head -1 | awk '{ print $2}')
else
SERVER_IP=$(curl -4 https://ifconfig.me 2> /dev/null || hostname -I | awk '{ print $1}')
fi
echo "Copy-paste those lines on backup server(s) :"
echo "----------"
echo "SERVER_NAME=${SERVER_NAME}"
echo "SERVER_IP=${SERVER_IP}"
echo "echo '${SSH_KEY}' > /root/\${SERVER_NAME}.pub"
echo "bkctld init \${SERVER_NAME}"
echo "bkctld key \${SERVER_NAME} /root/\${SERVER_NAME}.pub"
echo "bkctld ip \${SERVER_NAME} \${SERVER_IP}"
echo "bkctld start \${SERVER_NAME}"
echo "bkctld status \${SERVER_NAME}"
echo "grep --quiet --extended-regexp \"^\\s?NODE=\" /etc/default/bkctld && bkctld sync \${SERVER_NAME}"
echo "----------"
}
copy_template() {
dest_path=${1}
dest_dir="$(dirname "${dest_path}")"
if [ -e "${dest_path}" ]; then
printf "Path for new evobackup script '%s' already exists.\n" "${dest_path}" >&2
exit 1
elif [ ! -e "${dest_dir}" ]; then
printf "Parent directory '%s' doesn't exist. Create it first.\n" "${dest_dir}" >&2
exit 1
else
if cp "${LIBDIR}/zzz_evobackup.sh" "${dest_path}"; then
chmod 750 "${dest_path}"
# Insert metadata about the template
sed -i "s|@COMMAND@|${PROGPATH} ${ARGS}|" "${dest_path}"
sed -i "s|@DATE@|$(date --iso-8601=seconds)|" "${dest_path}"
sed -i "s|@VERSION@|${VERSION}|" "${dest_path}"
# Make sure that the library directory is correct
sed -i "s|^LIBDIR=.\+|LIBDIR=\"${LIBDIR}\"|" "${dest_path}"
printf "New evobackup script has been saved to '%s'.\n" "${dest_path}"
printf "Remember to customize it (mail notifications, backup servers…).\n"
exit 0
fi
fi
}
main() {
# If no argument is provided, print help and exit
# shellcheck disable=SC2086
if [ -z "${ARGS}" ]; then
show_help
exit 0
fi
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
-V|--version)
show_version
exit 0
;;
-h|--help)
show_help
exit 0
;;
--jail-init-commands)
jail_init_commands
exit 0
;;
--copy-template)
# copy-template option, with value separated by space
if [ -n "$2" ]; then
copy_template "${2}"
shift
else
printf "'%s' requires a non-empty option argument.\n" "--copy-template" >&2
exit 1
fi
;;
--copy-template=?*)
# copy-template option, with value separated by =
copy_template "${1#*=}"
;;
--copy-template=)
# copy-template option, without value
printf "'%s' requires a non-empty option argument.\n" "--copy-template" >&2
exit 1
;;
--)
# End of all options.
shift
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
printf "unknown option '%s'.\n" "${1}" >&2
exit 1
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
}
main ${ARGS}

View file

@ -0,0 +1,301 @@
#!/usr/bin/env bash
# shellcheck disable=SC2034,SC2317,SC2155
#######################################################################
# Snapshot Elasticsearch data
#
# Arguments:
# --protocol=<http|https> (default: http)
# --cacert=[String] (default: <none>)
# path to the CA certificate to use when using https
# --host=[String] (default: localhost)
# --port=[Integer] (default: 9200)
# --user=[String] (default: <none>)
# --password=[String] (default: <none>)
# --repository=[String] (default: snaprepo)
# --snapshot=[String] (default: snapshot.daily)
#######################################################################
dump_elasticsearch() {
local option_protocol="http"
local option_cacert=""
local option_host="localhost"
local option_port="9200"
local option_user=""
local option_password=""
local option_repository="snaprepo"
local option_snapshot="snapshot.daily"
local option_others=""
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
--protocol)
# protocol options, with value separated by space
if [ -n "$2" ]; then
option_protocol="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--protocol' requires a non-empty option argument."
exit 1
fi
;;
--protocol=?*)
# protocol options, with value separated by =
option_protocol="${1#*=}"
;;
--protocol=)
# protocol options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--protocol' requires a non-empty option argument."
exit 1
;;
--cacert)
# cacert options, with value separated by space
if [ -n "$2" ]; then
option_cacert="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--cacert' requires a non-empty option argument."
exit 1
fi
;;
--cacert=?*)
# cacert options, with value separated by =
option_cacert="${1#*=}"
;;
--cacert=)
# cacert options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--cacert' requires a non-empty option argument."
exit 1
;;
--host)
# host options, with value separated by space
if [ -n "$2" ]; then
option_host="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--host' requires a non-empty option argument."
exit 1
fi
;;
--host=?*)
# host options, with value separated by =
option_host="${1#*=}"
;;
--host=)
# host options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--host' requires a non-empty option argument."
exit 1
;;
--port)
# port options, with value separated by space
if [ -n "$2" ]; then
option_port="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
fi
;;
--port=?*)
# port options, with value separated by =
option_port="${1#*=}"
;;
--port=)
# port options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
;;
--user)
# user options, with value separated by space
if [ -n "$2" ]; then
option_user="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
fi
;;
--user=?*)
# user options, with value separated by =
option_user="${1#*=}"
;;
--user=)
# user options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
;;
--password)
# password options, with value separated by space
if [ -n "$2" ]; then
option_password="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
fi
;;
--password=?*)
# password options, with value separated by =
option_password="${1#*=}"
;;
--password=)
# password options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
;;
--repository)
# repository options, with value separated by space
if [ -n "$2" ]; then
option_repository="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--repository' requires a non-empty option argument."
exit 1
fi
;;
--repository=?*)
# repository options, with value separated by =
option_repository="${1#*=}"
;;
--repository=)
# repository options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--repository' requires a non-empty option argument."
exit 1
;;
--snapshot)
# snapshot options, with value separated by space
if [ -n "$2" ]; then
option_snapshot="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--snapshot' requires a non-empty option argument."
exit 1
fi
;;
--snapshot=?*)
# snapshot options, with value separated by =
option_snapshot="${1#*=}"
;;
--snapshot=)
# snapshot options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--snapshot' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
option_others=${*}
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
# Use the default Elasticsearch CA certificate when using HTTPS, if not specified directly
local default_cacert="/etc/elasticsearch/certs/http_ca.crt"
if [ "${option_protocol}" = "https" ] && [ -z "${option_cacert}" ] && [ -f "${default_cacert}" ]; then
option_cacert="${default_cacert}"
fi
local errors_dir="${ERRORS_DIR}/elasticsearch-${option_repository}-${option_snapshot}"
rm -rf "${errors_dir}"
mkdir -p "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${errors_dir}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${option_snapshot}"
## Take a snapshot as a backup.
## Warning: You need to have a path.repo configured.
## See: https://wiki.evolix.org/HowtoElasticsearch#snapshots-et-sauvegardes
local base_url="${option_protocol}://${option_host}:${option_port}"
local repository_url="${base_url}/_snapshot/${option_repository}"
local snapshot_url="${repository_url}/${option_snapshot}"
# Verify snapshot repository
local error_file="${errors_dir}/verify.err"
declare -a connect_options
connect_options=()
if [ -n "${option_cacert}" ]; then
connect_options+=(--cacert "${option_cacert}")
fi
if [ -n "${option_user}" ] || [ -n "${option_password}" ]; then
local connect_options+=("--user ${option_user}:${option_password}")
fi
if [ -n "${option_others}" ]; then
# word splitting is deliberate here
# shellcheck disable=SC2206
connect_options+=(${option_others})
fi
# Add the http return code at the end of the output
connect_options+=(--write-out '%{http_code}\n')
connect_options+=(--silent)
declare -a dump_options
dump_options=()
dump_options+=(--request POST)
dump_cmd="curl ${connect_options[*]} ${dump_options[*]} ${repository_url}/_verify?pretty"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd} > "${error_file}"
# test if the last line of the log file is "200"
tail -n 1 "${error_file}" | grep --quiet "^200$" "${error_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: repository verification returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
# Delete snapshot
declare -a dump_options
dump_options=()
dump_options+=(--request DELETE)
dump_cmd="curl ${connect_options[*]} ${dump_options[*]} ${snapshot_url}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd} > /dev/null
# Create snapshot
local error_file="${errors_dir}/create.err"
declare -a dump_options
dump_options=()
dump_options+=(--request PUT)
dump_cmd="curl ${connect_options[*]} ${dump_options[*]} ${snapshot_url}?wait_for_completion=true"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd} > "${error_file}"
# test if the last line of the log file is "200"
tail -n 1 "${error_file}" | grep --quiet "^200$" "${error_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: curl returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${option_snapshot}"
}

View file

@ -0,0 +1,559 @@
#!/usr/bin/env bash
# shellcheck disable=SC2034,SC2317,SC2155
#######################################################################
# Dump LDAP files (config, data, all)
#
# Arguments: <none>
#######################################################################
dump_ldap() {
## OpenLDAP : example with slapcat
local dump_dir="${LOCAL_BACKUP_DIR}/ldap"
rm -rf "${dump_dir}"
mkdir -p "${dump_dir}"
chmod 700 "${dump_dir}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${FUNCNAME[0]} to ${dump_dir}"
dump_cmd="slapcat -n 0 -l ${dump_dir}/config.bak"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd}
dump_cmd="slapcat -n 1 -l ${dump_dir}/data.bak"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd}
dump_cmd="slapcat -l ${dump_dir}/all.bak"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd}
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${FUNCNAME[0]}"
}
#######################################################################
# Copy dump file of Redis instances
#
# Arguments:
# --instances=[Integer] (default: all)
#######################################################################
dump_redis() {
all_instances=$(find /var/lib/ -mindepth 1 -maxdepth 1 '(' -type d -o -type l ')' -name 'redis*')
local option_instances=""
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
--instances)
# instances options, with key and value separated by space
if [ -n "$2" ]; then
if [ "${2}" == "all" ]; then
read -a option_instances <<< "${all_instances}"
else
IFS="," read -a option_instances <<< "${2}"
fi
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--instances' requires a non-empty option argument."
exit 1
fi
;;
--instances=?*)
# instances options, with key and value separated by =
if [ "${1#*=}" == "all" ]; then
read -a option_instances <<< "${all_instances}"
else
IFS="," read -a option_instances <<< "${1#*=}"
fi
;;
--instances=)
# instances options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--instances' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
for instance in "${option_instances[@]}"; do
name=$(basename "${instance}")
local dump_dir="${LOCAL_BACKUP_DIR}/${name}"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
if [ -f "${instance}/dump.rdb" ]; then
local error_file="${errors_dir}/${name}.err"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_dir}"
# Copy the Redis database
dump_cmd="cp -a ${instance}/dump.rdb ${dump_dir}/dump.rdb"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd} 2> "${error_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: cp ${instance}/dump.rdb to ${dump_dir} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
# Compress the Redis database
dump_cmd="gzip ${dump_dir}/dump.rdb"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd}
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: gzip ${dump_dir}/dump.rdb returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_dir}"
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '${instance}/dump.rdb' not found."
fi
done
}
#######################################################################
# Dump all collections of a MongoDB database
# using a custom authentication, instead of /etc/mysql/debian.cnf
#
# Arguments:
# --port=[String] (default: <blank>)
# --user=[String] (default: <blank>)
# --password=[String] (default: <blank>)
# --dump-label=[String] (default: "default")
# used as suffix of the dump dir to differenciate multiple instances
# Other options after -- are passed as-is to mongodump
#
# don't forget to create use with read-only access
# > use admin
# > db.createUser( { user: "mongobackup", pwd: "PASS", roles: [ "backup", ] } )
#######################################################################
dump_mongodb() {
local option_port=""
local option_user=""
local option_password=""
local option_dump_label=""
local option_others=""
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
--port)
# port options, with value separated by space
if [ -n "$2" ]; then
option_port="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
fi
;;
--port=?*)
# port options, with value separated by =
option_port="${1#*=}"
;;
--port=)
# port options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
;;
--user)
# user options, with value separated by space
if [ -n "$2" ]; then
option_user="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
fi
;;
--user=?*)
# user options, with value separated by =
option_user="${1#*=}"
;;
--user=)
# user options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
;;
--password)
# password options, with value separated by space
if [ -n "$2" ]; then
option_password="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
fi
;;
--password=?*)
# password options, with value separated by =
option_password="${1#*=}"
;;
--password=)
# password options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
;;
--dump-label)
# dump-label options, with value separated by space
if [ -n "$2" ]; then
option_dump_label="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
fi
;;
--dump-label=?*)
# dump-label options, with value separated by =
option_dump_label="${1#*=}"
;;
--dump-label=)
# dump-label options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
option_others=${*}
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
if [ -z "${option_dump_label}" ]; then
if [ -n "${option_port}" ]; then
option_dump_label="${option_port}"
else
option_dump_label="default"
fi
fi
local dump_dir="${LOCAL_BACKUP_DIR}/mongodb-${option_dump_label}"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
local error_file="${errors_dir}.err"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_dir}"
declare -a dump_options
dump_options=()
if [ -n "${option_port}" ]; then
dump_options+=(--port="${option_port}")
fi
if [ -n "${option_user}" ]; then
dump_options+=(--username="${option_user}")
fi
if [ -n "${option_password}" ]; then
dump_options+=(--password="${option_password}")
fi
dump_options+=(--out="${dump_dir}/")
if [ -n "${option_others}" ]; then
# word splitting is deliberate here
# shellcheck disable=SC2206
dump_options+=(${option_others})
fi
dump_cmd="mongodump ${dump_options[*]}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd} > /dev/null"
${dump_cmd} 2> "${error_file}" > /dev/null
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: mongodump to ${dump_dir} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - stop ${FUNCNAME[0]}: ${dump_dir}"
}
#######################################################################
# Dump RAID configuration
#
# Arguments: <none>
#######################################################################
dump_raid_config() {
local dump_dir="${LOCAL_BACKUP_DIR}/raid"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
if command -v megacli > /dev/null; then
local error_file="${errors_dir}/megacli.cfg"
local dump_file="${dump_dir}/megacli.err"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
dump_cmd="megacli -CfgSave -f ${dump_file} -a0"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd} 2> "${error_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: megacli to ${dump_file} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
elif command -v perccli > /dev/null; then
local error_file="${errors_dir}/perccli.cfg"
local dump_file="${dump_dir}/perccli.err"
# log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
# TODO: find out what the correct command is
# dump_cmd="perccli XXXX"
# log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
# ${dump_cmd} 2> ${error_file}
# local last_rc=$?
# # shellcheck disable=SC2086
# if [ ${last_rc} -ne 0 ]; then
# log_error "LOCAL_TASKS - ${FUNCNAME[0]}: perccli to ${dump_file} returned an error ${last_rc}" "${error_file}"
# GLOBAL_RC=${E_DUMPFAILED}
# else
# rm -f "${error_file}"
# fi
# log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
else
log "LOCAL_TASKS - ${FUNCNAME[0]}: 'megacli' and 'perccli' not found, unable to dump RAID configuration"
fi
}
#######################################################################
# Save some traceroute/mtr results
#
# Arguments:
# --targets=[IP,HOST] (default: <none>)
#######################################################################
dump_traceroute() {
local option_targets=""
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
--targets)
# targets options, with key and value separated by space
if [ -n "$2" ]; then
IFS="," read -a option_targets <<< "${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--targets' requires a non-empty option argument."
exit 1
fi
;;
--targets=?*)
# targets options, with key and value separated by =
IFS="," read -a option_targets <<< "${1#*=}"
;;
--targets=)
# targets options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--targets' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
local dump_dir="${LOCAL_BACKUP_DIR}/traceroute"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
mtr_bin=$(command -v mtr)
if [ -n "${mtr_bin}" ]; then
for target in "${option_targets[@]}"; do
local dump_file="${dump_dir}/mtr-${target}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
${mtr_bin} -r "${target}" > "${dump_file}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
done
fi
traceroute_bin=$(command -v traceroute)
if [ -n "${traceroute_bin}" ]; then
for target in "${option_targets[@]}"; do
local dump_file="${dump_dir}/traceroute-${target}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
${traceroute_bin} -n "${target}" > "${dump_file}" 2>&1
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
done
fi
}
#######################################################################
# Save many system information, using dump_server_state
#
# Arguments:
# any option for dump-server-state (except --dump-dir) is usable
# (default: --all)
#######################################################################
dump_server_state() {
local dump_dir="${LOCAL_BACKUP_DIR}/server-state"
rm -rf "${dump_dir}"
# Do not create the directory
# mkdir -p -m 700 "${dump_dir}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_dir}"
# pass all options
read -a options <<< "${@}"
# if no option is given, use "--all" as fallback
if [ ${#options[@]} -le 0 ]; then
options=(--all)
fi
# add "--dump-dir" in case it is missing (as it should)
options+=(--dump-dir "${dump_dir}")
dump_server_state_bin=$(command -v dump-server-state)
if [ -z "${dump_server_state_bin}" ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: dump-server-state is missing"
rc=1
else
dump_cmd="${dump_server_state_bin} ${options[*]}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd}
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: dump-server-state returned an error ${last_rc}, check ${dump_dir}"
GLOBAL_RC=${E_DUMPFAILED}
fi
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_dir}"
}
#######################################################################
# Save RabbitMQ data
#
# Arguments: <none>
#
# Warning: This has been poorly tested
#######################################################################
dump_rabbitmq() {
local dump_dir="${LOCAL_BACKUP_DIR}/rabbitmq"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
local error_file="${errors_dir}.err"
local dump_file="${dump_dir}/config"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
dump_cmd="rabbitmqadmin export ${dump_file}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd} 2> "${error_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: pg_dump to ${dump_file} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
}
#######################################################################
# Save Files ACL on various partitions.
#
# Arguments: <none>
#######################################################################
dump_facl() {
local dump_dir="${LOCAL_BACKUP_DIR}/facl"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_dir}"
dump_cmd="getfacl -R /etc > ${dump_dir}/etc.txt"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd}
dump_cmd="getfacl -R /home > ${dump_dir}/home.txt"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd}
dump_cmd="getfacl -R /usr > ${dump_dir}/usr.txt"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd}
dump_cmd="getfacl -R /var > ${dump_dir}/var.txt"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd}
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_dir}"
}

View file

@ -0,0 +1,1551 @@
#!/usr/bin/env bash
# shellcheck disable=SC2034,SC2317,SC2155
#######################################################################
# Dump complete summary of an instance (using pt-mysql-summary)
#
# Arguments:
# --port=[Integer] (default: <blank>)
# --socket=[String] (default: <blank>)
# --user=[String] (default: <blank>)
# --password=[String] (default: <blank>)
# --defaults-file=[String] (default: <blank>)
# --defaults-extra-file=[String] (default: <blank>)
# --defaults-group-suffix=[String] (default: <blank>)
# --dump-label=[String] (default: "default")
# used as suffix of the dump dir to differenciate multiple instances
#######################################################################
dump_mysql_summary() {
local option_port=""
local option_socket=""
local option_defaults_file=""
local option_defaults_extra_file=""
local option_defaults_group_suffix=""
local option_user=""
local option_password=""
local option_dump_label=""
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
--defaults-file)
# defaults-file options, with value separated by space
if [ -n "$2" ]; then
option_defaults_file="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
fi
;;
--defaults-file=?*)
# defaults-file options, with value separated by =
option_defaults_file="${1#*=}"
;;
--defaults-file=)
# defaults-file options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
;;
--defaults-extra-file)
# defaults-file options, with value separated by space
if [ -n "$2" ]; then
option_defaults_extra_file="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
fi
;;
--defaults-extra-file=?*)
# defaults-extra-file options, with value separated by =
option_defaults_extra_file="${1#*=}"
;;
--defaults-extra-file=)
# defaults-extra-file options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-extra-file' requires a non-empty option argument."
exit 1
;;
--defaults-group-suffix)
# defaults-group-suffix options, with value separated by space
if [ -n "$2" ]; then
option_defaults_group_suffix="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-group-suffix' requires a non-empty option argument."
exit 1
fi
;;
--defaults-group-suffix=?*)
# defaults-group-suffix options, with value separated by =
option_defaults_group_suffix="${1#*=}"
;;
--defaults-group-suffix=)
# defaults-group-suffix options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-group-suffix' requires a non-empty option argument."
exit 1
;;
--port)
# port options, with value separated by space
if [ -n "$2" ]; then
option_port="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
fi
;;
--port=?*)
# port options, with value separated by =
option_port="${1#*=}"
;;
--port=)
# port options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
;;
--socket)
# socket options, with value separated by space
if [ -n "$2" ]; then
option_socket="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
exit 1
fi
;;
--socket=?*)
# socket options, with value separated by =
option_socket="${1#*=}"
;;
--socket=)
# socket options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
exit 1
;;
--user)
# user options, with value separated by space
if [ -n "$2" ]; then
option_user="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
fi
;;
--user=?*)
# user options, with value separated by =
option_user="${1#*=}"
;;
--user=)
# user options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
;;
--password)
# password options, with value separated by space
if [ -n "$2" ]; then
option_password="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
fi
;;
--password=?*)
# password options, with value separated by =
option_password="${1#*=}"
;;
--password=)
# password options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
;;
--dump-label)
# dump-label options, with value separated by space
if [ -n "$2" ]; then
option_dump_label="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
fi
;;
--dump-label=?*)
# dump-label options, with value separated by =
option_dump_label="${1#*=}"
;;
--dump-label=)
# dump-label options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unkwnown option (ignored): '${1}'"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
if [ -z "${option_dump_label}" ]; then
if [ -n "${option_defaults_group_suffix}" ]; then
option_dump_label="${option_defaults_group_suffix}"
elif [ -n "${option_port}" ]; then
option_dump_label="${option_port}"
elif [ -n "${option_socket}" ]; then
option_dump_label=$(path_to_str "${option_socket}")
else
option_dump_label="default"
fi
fi
local dump_dir="${LOCAL_BACKUP_DIR}/mysql-${option_dump_label}-summary"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
## Dump all grants (requires 'percona-toolkit' package)
if command -v pt-mysql-summary > /dev/null; then
local error_file="${errors_dir}/mysql-summary.err"
local dump_file="${dump_dir}/mysql-summary.out"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
## Connection options
declare -a connect_options
connect_options=()
if [ -n "${option_defaults_file}" ]; then
connect_options+=(--defaults-file="${option_defaults_file}")
fi
if [ -n "${option_defaults_extra_file}" ]; then
connect_options+=(--defaults-extra-file="${option_defaults_extra_file}")
fi
if [ -n "${option_defaults_group_suffix}" ]; then
connect_options+=(--defaults-group-suffix="${option_defaults_group_suffix}")
fi
if [ -n "${option_port}" ]; then
connect_options+=(--protocol=tcp)
connect_options+=(--port="${option_port}")
fi
if [ -n "${option_socket}" ]; then
connect_options+=(--protocol=socket)
connect_options+=(--socket="${option_socket}")
fi
if [ -n "${option_user}" ]; then
connect_options+=(--user="${option_user}")
fi
if [ -n "${option_password}" ]; then
connect_options+=(--password="${option_password}")
fi
declare -a options
options=()
options+=(--sleep=0)
dump_cmd="pt-mysql-summary ${options[*]} -- ${connect_options[*]}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd} 2> "${error_file}" > "${dump_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: pt-mysql-summary to ${dump_file} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
else
log "LOCAL_TASKS - ${FUNCNAME[0]}: 'pt-mysql-summary' not found, unable to dump summary"
fi
}
#######################################################################
# Dump grants of an instance
#
# Arguments:
# --port=[Integer] (default: <blank>)
# --socket=[String] (default: <blank>)
# --user=[String] (default: <blank>)
# --password=[String] (default: <blank>)
# --defaults-file=[String] (default: <blank>)
# --dump-label=[String] (default: "default")
# used as suffix of the dump dir to differenciate multiple instances
#######################################################################
dump_mysql_grants() {
local option_port=""
local option_socket=""
local option_defaults_file=""
local option_user=""
local option_password=""
local option_dump_label=""
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
--defaults-file)
# defaults-file options, with value separated by space
if [ -n "$2" ]; then
option_defaults_file="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
fi
;;
--defaults-file=?*)
# defaults-file options, with value separated by =
option_defaults_file="${1#*=}"
;;
--defaults-file=)
# defaults-file options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
;;
--port)
# port options, with value separated by space
if [ -n "$2" ]; then
option_port="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
fi
;;
--port=?*)
# port options, with value separated by =
option_port="${1#*=}"
;;
--port=)
# port options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
;;
--socket)
# socket options, with value separated by space
if [ -n "$2" ]; then
option_socket="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
exit 1
fi
;;
--socket=?*)
# socket options, with value separated by =
option_socket="${1#*=}"
;;
--socket=)
# socket options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
exit 1
;;
--user)
# user options, with value separated by space
if [ -n "$2" ]; then
option_user="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
fi
;;
--user=?*)
# user options, with value separated by =
option_user="${1#*=}"
;;
--user=)
# user options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
;;
--password)
# password options, with value separated by space
if [ -n "$2" ]; then
option_password="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
fi
;;
--password=?*)
# password options, with value separated by =
option_password="${1#*=}"
;;
--password=)
# password options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
;;
--dump-label)
# dump-label options, with value separated by space
if [ -n "$2" ]; then
option_dump_label="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
fi
;;
--dump-label=?*)
# dump-label options, with value separated by =
option_dump_label="${1#*=}"
;;
--dump-label=)
# dump-label options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
if [ -z "${option_dump_label}" ]; then
if [ -n "${option_port}" ]; then
option_dump_label="${option_port}"
elif [ -n "${option_socket}" ]; then
option_dump_label=$(path_to_str "${option_socket}")
else
option_dump_label="default"
fi
fi
local dump_dir="${LOCAL_BACKUP_DIR}/mysql-${option_dump_label}-grants"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
## Dump all grants (requires 'percona-toolkit' package)
if command -v pt-show-grants > /dev/null; then
local error_file="${errors_dir}/all_grants.err"
local dump_file="${dump_dir}/all_grants.sql"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
declare -a options
options=()
if [ -n "${option_defaults_file}" ]; then
options+=(--defaults-file="${option_defaults_file}")
fi
if [ -n "${option_port}" ]; then
options+=(--port="${option_port}")
fi
if [ -n "${option_socket}" ]; then
options+=(--socket="${option_socket}")
fi
if [ -n "${option_user}" ]; then
options+=(--user="${option_user}")
fi
if [ -n "${option_password}" ]; then
options+=(--password="${option_password}")
fi
options+=(--flush)
options+=(--no-header)
dump_cmd="pt-show-grants ${options[*]}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd} 2> "${error_file}" > "${dump_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: pt-show-grants to ${dump_file} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
else
log "LOCAL_TASKS - ${FUNCNAME[0]}: 'pt-show-grants' not found, unable to dump grants"
fi
}
#######################################################################
# Dump a single compressed file of all databases of an instance
# and a file containing only the schema.
#
# Arguments:
# --masterdata (default: <absent>)
# --port=[Integer] (default: <blank>)
# --socket=[String] (default: <blank>)
# --user=[String] (default: <blank>)
# --password=[String] (default: <blank>)
# --defaults-file=[String] (default: <blank>)
# --defaults-extra-file=[String] (default: <blank>)
# --defaults-group-suffix=[String] (default: <blank>)
# --dump-label=[String] (default: "default")
# used as suffix of the dump dir to differenciate multiple instances
# --compress=<gzip|pigz|bzip2|xz|none> (default: "gzip")
# Other options after -- are passed as-is to mysqldump
#######################################################################
dump_mysql_global() {
local option_masterdata=""
local option_port=""
local option_socket=""
local option_defaults_file=""
local option_defaults_extra_file=""
local option_defaults_group_suffix=""
local option_user=""
local option_password=""
local option_dump_label=""
local option_compress=""
local option_others=""
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
--masterdata)
option_masterdata="--masterdata"
;;
--defaults-file)
# defaults-file options, with value separated by space
if [ -n "$2" ]; then
option_defaults_file="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
fi
;;
--defaults-file=?*)
# defaults-file options, with value separated by =
option_defaults_file="${1#*=}"
;;
--defaults-file=)
# defaults-file options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
;;
--defaults-extra-file)
# defaults-file options, with value separated by space
if [ -n "$2" ]; then
option_defaults_extra_file="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
fi
;;
--defaults-extra-file=?*)
# defaults-extra-file options, with value separated by =
option_defaults_extra_file="${1#*=}"
;;
--defaults-extra-file=)
# defaults-extra-file options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-extra-file' requires a non-empty option argument."
exit 1
;;
--defaults-group-suffix)
# defaults-group-suffix options, with value separated by space
if [ -n "$2" ]; then
option_defaults_group_suffix="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-group-suffix' requires a non-empty option argument."
exit 1
fi
;;
--defaults-group-suffix=?*)
# defaults-group-suffix options, with value separated by =
option_defaults_group_suffix="${1#*=}"
;;
--defaults-group-suffix=)
# defaults-group-suffix options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-group-suffix' requires a non-empty option argument."
exit 1
;;
--port)
# port options, with value separated by space
if [ -n "$2" ]; then
option_port="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
fi
;;
--port=?*)
# port options, with value separated by =
option_port="${1#*=}"
;;
--port=)
# port options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
;;
--socket)
# socket options, with value separated by space
if [ -n "$2" ]; then
option_socket="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
exit 1
fi
;;
--socket=?*)
# socket options, with value separated by =
option_socket="${1#*=}"
;;
--socket=)
# socket options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
exit 1
;;
--user)
# user options, with value separated by space
if [ -n "$2" ]; then
option_user="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
fi
;;
--user=?*)
# user options, with value separated by =
option_user="${1#*=}"
;;
--user=)
# user options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
;;
--password)
# password options, with value separated by space
if [ -n "$2" ]; then
option_password="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
fi
;;
--password=?*)
# password options, with value separated by =
option_password="${1#*=}"
;;
--password=)
# password options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
;;
--dump-label)
# dump-label options, with value separated by space
if [ -n "$2" ]; then
option_dump_label="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
fi
;;
--dump-label=?*)
# dump-label options, with value separated by =
option_dump_label="${1#*=}"
;;
--dump-label=)
# dump-label options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
;;
--compress)
# compress options, with value separated by space
if [ -n "$2" ]; then
option_compress="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
exit 1
fi
;;
--compress=?*)
# compress options, with value separated by =
option_compress="${1#*=}"
;;
--compress=)
# compress options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
option_others=${*}
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
case "${option_compress}" in
none)
compress_cmd="cat"
dump_ext=""
;;
bzip2|bz|bz2)
compress_cmd="bzip2 --best"
dump_ext=".bz"
;;
xz)
compress_cmd="xz --best"
dump_ext=".xz"
;;
pigz)
compress_cmd="pigz --best"
dump_ext=".gz"
;;
gz|gzip|*)
compress_cmd="gzip --best"
dump_ext=".gz"
;;
esac
if [ -z "${option_dump_label}" ]; then
if [ -n "${option_defaults_group_suffix}" ]; then
option_dump_label="${option_defaults_group_suffix}"
elif [ -n "${option_port}" ]; then
option_dump_label="${option_port}"
elif [ -n "${option_socket}" ]; then
option_dump_label=$(path_to_str "${option_socket}")
else
option_dump_label="default"
fi
fi
## Connection options
declare -a connect_options
connect_options=()
if [ -n "${option_defaults_file}" ]; then
connect_options+=(--defaults-file="${option_defaults_file}")
fi
if [ -n "${option_defaults_extra_file}" ]; then
connect_options+=(--defaults-extra-file="${option_defaults_extra_file}")
fi
if [ -n "${option_defaults_group_suffix}" ]; then
connect_options+=(--defaults-group-suffix="${option_defaults_group_suffix}")
fi
if [ -n "${option_port}" ]; then
connect_options+=(--protocol=tcp)
connect_options+=(--port="${option_port}")
fi
if [ -n "${option_socket}" ]; then
connect_options+=(--protocol=socket)
connect_options+=(--socket="${option_socket}")
fi
if [ -n "${option_user}" ]; then
connect_options+=(--user="${option_user}")
fi
if [ -n "${option_password}" ]; then
connect_options+=(--password="${option_password}")
fi
## Global all databases in one file
local dump_dir="${LOCAL_BACKUP_DIR}/mysql-${option_dump_label}"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
local error_file="${errors_dir}/mysqldump.err"
local dump_file="${dump_dir}/mysqldump.sql${dump_ext}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
declare -a dump_options
dump_options=()
dump_options+=(--opt)
dump_options+=(--force)
dump_options+=(--events)
dump_options+=(--hex-blob)
dump_options+=(--all-databases)
if [ -n "${option_masterdata}" ]; then
dump_options+=("${option_masterdata}")
fi
if [ -n "${option_others}" ]; then
# word splitting is deliberate here
# shellcheck disable=SC2206
dump_options+=(${option_others})
fi
## WARNING : logging and executing the command must be separate
## because otherwise Bash would interpret | and > as strings and not syntax.
dump_cmd="mysqldump ${connect_options[*]} ${dump_options[*]} 2> ${error_file} | ${compress_cmd} > ${dump_file}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
mysqldump "${connect_options[@]}" "${dump_options[@]}" 2> "${error_file}" | ${compress_cmd} > "${dump_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: mysqldump returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
## Schema only (no data) for each databases
local error_file="${errors_dir}/mysqldump.schema.err"
local dump_file="${dump_dir}/mysqldump.schema.sql"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
declare -a dump_options
dump_options=()
dump_options+=(--force)
dump_options+=(--no-data)
dump_options+=(--all-databases)
if [ -n "${option_others}" ]; then
# word splitting is deliberate here
# shellcheck disable=SC2206
dump_options+=(${option_others})
fi
dump_cmd="mysqldump ${connect_options[*]} ${dump_options[*]}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd} 2> "${error_file}" > "${dump_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: mysqldump returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
}
#######################################################################
# Dump a file of each databases of an instance
# and a file containing only the schema.
#
# Arguments:
# --port=[Integer] (default: <blank>)
# --socket=[String] (default: <blank>)
# --user=[String] (default: <blank>)
# --password=[String] (default: <blank>)
# --defaults-file=[String] (default: <blank>)
# --defaults-extra-file=[String] (default: <blank>)
# --defaults-group-suffix=[String] (default: <blank>)
# --dump-label=[String] (default: "default")
# used as suffix of the dump dir to differenciate multiple instances
# --compress=<gzip|pigz|bzip2|xz|none> (default: "gzip")
# Other options after -- are passed as-is to mysqldump
#######################################################################
dump_mysql_per_base() {
local option_port=""
local option_socket=""
local option_defaults_file=""
local option_defaults_extra_file=""
local option_defaults_group_suffix=""
local option_user=""
local option_password=""
local option_dump_label=""
local option_compress=""
local option_others=""
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
--defaults-file)
# defaults-file options, with value separated by space
if [ -n "$2" ]; then
option_defaults_file="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
fi
;;
--defaults-file=?*)
# defaults-file options, with value separated by =
option_defaults_file="${1#*=}"
;;
--defaults-file=)
# defaults-file options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
;;
--defaults-extra-file)
# defaults-file options, with value separated by space
if [ -n "$2" ]; then
option_defaults_extra_file="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
fi
;;
--defaults-extra-file=?*)
# defaults-extra-file options, with value separated by =
option_defaults_extra_file="${1#*=}"
;;
--defaults-extra-file=)
# defaults-extra-file options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-extra-file' requires a non-empty option argument."
exit 1
;;
--defaults-group-suffix)
# defaults-group-suffix options, with value separated by space
if [ -n "$2" ]; then
option_defaults_group_suffix="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-group-suffix' requires a non-empty option argument."
exit 1
fi
;;
--defaults-group-suffix=?*)
# defaults-group-suffix options, with value separated by =
option_defaults_group_suffix="${1#*=}"
;;
--defaults-group-suffix=)
# defaults-group-suffix options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-group-suffix' requires a non-empty option argument."
exit 1
;;
--port)
# port options, with value separated by space
if [ -n "$2" ]; then
option_port="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
fi
;;
--port=?*)
# port options, with value separated by =
option_port="${1#*=}"
;;
--port=)
# port options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
;;
--socket)
# socket options, with value separated by space
if [ -n "$2" ]; then
option_socket="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
exit 1
fi
;;
--socket=?*)
# socket options, with value separated by =
option_socket="${1#*=}"
;;
--socket=)
# socket options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
exit 1
;;
--user)
# user options, with value separated by space
if [ -n "$2" ]; then
option_user="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
fi
;;
--user=?*)
# user options, with value separated by =
option_user="${1#*=}"
;;
--user=)
# user options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
;;
--password)
# password options, with value separated by space
if [ -n "$2" ]; then
option_password="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
fi
;;
--password=?*)
# password options, with value separated by =
option_password="${1#*=}"
;;
--password=)
# password options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
;;
--dump-label)
# dump-label options, with value separated by space
if [ -n "$2" ]; then
option_dump_label="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
fi
;;
--dump-label=?*)
# dump-label options, with value separated by =
option_dump_label="${1#*=}"
;;
--dump-label=)
# dump-label options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
;;
--compress)
# compress options, with value separated by space
if [ -n "$2" ]; then
option_compress="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
exit 1
fi
;;
--compress=?*)
# compress options, with value separated by =
option_compress="${1#*=}"
;;
--compress=)
# compress options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
option_others=${*}
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
case "${option_compress}" in
none)
compress_cmd="cat"
dump_ext=""
;;
bzip2|bz|bz2)
compress_cmd="bzip2 --best"
dump_ext=".bz"
;;
xz)
compress_cmd="xz --best"
dump_ext=".xz"
;;
pigz)
compress_cmd="pigz --best"
dump_ext=".gz"
;;
gz|gzip|*)
compress_cmd="gzip --best"
dump_ext=".gz"
;;
esac
if [ -z "${option_dump_label}" ]; then
if [ -n "${option_defaults_group_suffix}" ]; then
option_dump_label="${option_defaults_group_suffix}"
elif [ -n "${option_port}" ]; then
option_dump_label="${option_port}"
elif [ -n "${option_socket}" ]; then
option_dump_label=$(path_to_str "${option_socket}")
else
option_dump_label="default"
fi
fi
## Connection options
declare -a connect_options
connect_options=()
if [ -n "${option_defaults_file}" ]; then
connect_options+=(--defaults-file="${option_defaults_file}")
fi
if [ -n "${option_defaults_extra_file}" ]; then
connect_options+=(--defaults-extra-file="${option_defaults_extra_file}")
fi
if [ -n "${option_defaults_group_suffix}" ]; then
connect_options+=(--defaults-group-suffix="${option_defaults_group_suffix}")
fi
if [ -n "${option_port}" ]; then
connect_options+=(--protocol=tcp)
connect_options+=(--port="${option_port}")
fi
if [ -n "${option_socket}" ]; then
connect_options+=(--protocol=socket)
connect_options+=(--socket="${option_socket}")
fi
if [ -n "${option_user}" ]; then
connect_options+=(--user="${option_user}")
fi
if [ -n "${option_password}" ]; then
connect_options+=(--password="${option_password}")
fi
local dump_dir="${LOCAL_BACKUP_DIR}/mysql-${option_dump_label}-per-base"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
databases=$(mysql "${connect_options[@]}" --execute="show databases" --silent --skip-column-names \
| grep --extended-regexp --invert-match "^(Database|information_schema|performance_schema|sys)")
for database in ${databases}; do
local error_file="${errors_dir}/${database}.err"
local dump_file="${dump_dir}/${database}.sql${dump_ext}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
declare -a dump_options
dump_options=()
dump_options+=(--opt)
dump_options+=(--force)
dump_options+=(--events)
dump_options+=(--hex-blob)
dump_options+=(--databases "${database}")
if [ -n "${option_others}" ]; then
# word splitting is deliberate here
# shellcheck disable=SC2206
dump_options+=(${option_others})
fi
## WARNING : logging and executing the command must be separate
## because otherwise Bash would interpret | and > as strings and not syntax.
log "LOCAL_TASKS - ${FUNCNAME[0]}: mysqldump ${connect_options[*]} ${dump_options[*]} | ${compress_cmd} > ${dump_file}"
mysqldump "${connect_options[@]}" "${dump_options[@]}" 2> "${error_file}" | ${compress_cmd} > "${dump_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: mysqldump returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
## Schema only (no data) for each databases
local error_file="${errors_dir}/${database}.schema.err"
local dump_file="${dump_dir}/${database}.schema.sql"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
declare -a dump_options
dump_options=()
dump_options+=(--force)
dump_options+=(--no-data)
dump_options+=(--databases "${database}")
if [ -n "${option_others}" ]; then
# word splitting is deliberate here
# shellcheck disable=SC2206
dump_options+=(${option_others})
fi
dump_cmd="mysqldump ${connect_options[*]} ${dump_options[*]}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd} 2> "${error_file}" > "${dump_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: mysqldump returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
done
}
#######################################################################
# Dump "tabs style" separate schema/data for each database of an instance
#
# Arguments:
# --port=[Integer] (default: <blank>)
# --socket=[String] (default: <blank>)
# --user=[String] (default: <blank>)
# --password=[String] (default: <blank>)
# --defaults-file=[String] (default: <blank>)
# --defaults-extra-file=[String] (default: <blank>)
# --defaults-group-suffix=[String] (default: <blank>)
# --dump-label=[String] (default: "default")
# used as suffix of the dump dir to differenciate multiple instances
# --compress=<gzip|pigz|bzip2|xz|none> (default: "gzip")
# Other options after -- are passed as-is to mysqldump
#######################################################################
dump_mysql_tabs() {
local option_port=""
local option_socket=""
local option_defaults_file=""
local option_defaults_extra_file=""
local option_defaults_group_suffix=""
local option_user=""
local option_password=""
local option_dump_label=""
local option_compress=""
local option_others=""
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
--defaults-file)
# defaults-file options, with value separated by space
if [ -n "$2" ]; then
option_defaults_file="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
fi
;;
--defaults-file=?*)
# defaults-file options, with value separated by =
option_defaults_file="${1#*=}"
;;
--defaults-file=)
# defaults-file options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
;;
--defaults-extra-file)
# defaults-file options, with value separated by space
if [ -n "$2" ]; then
option_defaults_extra_file="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
exit 1
fi
;;
--defaults-extra-file=?*)
# defaults-extra-file options, with value separated by =
option_defaults_extra_file="${1#*=}"
;;
--defaults-extra-file=)
# defaults-extra-file options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-extra-file' requires a non-empty option argument."
exit 1
;;
--defaults-group-suffix)
# defaults-group-suffix options, with value separated by space
if [ -n "$2" ]; then
option_defaults_group_suffix="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-group-suffix' requires a non-empty option argument."
exit 1
fi
;;
--defaults-group-suffix=?*)
# defaults-group-suffix options, with value separated by =
option_defaults_group_suffix="${1#*=}"
;;
--defaults-group-suffix=)
# defaults-group-suffix options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-group-suffix' requires a non-empty option argument."
exit 1
;;
--port)
# port options, with value separated by space
if [ -n "$2" ]; then
option_port="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
fi
;;
--port=?*)
# port options, with value separated by =
option_port="${1#*=}"
;;
--port=)
# port options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
exit 1
;;
--socket)
# socket options, with value separated by space
if [ -n "$2" ]; then
option_socket="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
exit 1
fi
;;
--socket=?*)
# socket options, with value separated by =
option_socket="${1#*=}"
;;
--socket=)
# socket options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
exit 1
;;
--user)
# user options, with value separated by space
if [ -n "$2" ]; then
option_user="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
fi
;;
--user=?*)
# user options, with value separated by =
option_user="${1#*=}"
;;
--user=)
# user options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
exit 1
;;
--password)
# password options, with value separated by space
if [ -n "$2" ]; then
option_password="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
fi
;;
--password=?*)
# password options, with value separated by =
option_password="${1#*=}"
;;
--password=)
# password options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
exit 1
;;
--dump-label)
# dump-label options, with value separated by space
if [ -n "$2" ]; then
option_dump_label="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
fi
;;
--dump-label=?*)
# dump-label options, with value separated by =
option_dump_label="${1#*=}"
;;
--dump-label=)
# dump-label options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
;;
--compress)
# compress options, with value separated by space
if [ -n "$2" ]; then
option_compress="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
exit 1
fi
;;
--compress=?*)
# compress options, with value separated by =
option_compress="${1#*=}"
;;
--compress=)
# compress options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
option_others=${*}
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
case "${option_compress}" in
none)
compress_cmd="cat"
dump_ext=""
;;
bzip2|bz|bz2)
compress_cmd="bzip2 --best"
dump_ext=".bz"
;;
xz)
compress_cmd="xz --best"
dump_ext=".xz"
;;
pigz)
compress_cmd="pigz --best"
dump_ext=".gz"
;;
gz|gzip|*)
compress_cmd="gzip --best"
dump_ext=".gz"
;;
esac
if [ -z "${option_dump_label}" ]; then
if [ -n "${option_defaults_group_suffix}" ]; then
option_dump_label="${option_defaults_group_suffix}"
elif [ -n "${option_port}" ]; then
option_dump_label="${option_port}"
elif [ -n "${option_socket}" ]; then
option_dump_label=$(path_to_str "${option_socket}")
else
option_dump_label="default"
fi
fi
## Connection options
declare -a connect_options
connect_options=()
if [ -n "${option_defaults_file}" ]; then
connect_options+=(--defaults-file="${option_defaults_file}")
fi
if [ -n "${option_defaults_extra_file}" ]; then
connect_options+=(--defaults-extra-file="${option_defaults_extra_file}")
fi
if [ -n "${option_defaults_group_suffix}" ]; then
connect_options+=(--defaults-group-suffix="${option_defaults_group_suffix}")
fi
if [ -n "${option_port}" ]; then
connect_options+=(--protocol=tcp)
connect_options+=(--port="${option_port}")
fi
if [ -n "${option_socket}" ]; then
connect_options+=(--protocol=socket)
connect_options+=(--socket="${option_socket}")
fi
if [ -n "${option_user}" ]; then
connect_options+=(--user="${option_user}")
fi
if [ -n "${option_password}" ]; then
connect_options+=(--password="${option_password}")
fi
databases=$(mysql "${connect_options[@]}" --execute="show databases" --silent --skip-column-names \
| grep --extended-regexp --invert-match "^(Database|information_schema|performance_schema|sys)")
for database in ${databases}; do
local dump_dir="${LOCAL_BACKUP_DIR}/mysql-${option_dump_label}-tabs/${database}"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
chown -RL mysql "${dump_dir}"
local error_file="${errors_dir}.err"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_dir}"
declare -a dump_options
dump_options=()
dump_options+=(--force)
dump_options+=(--quote-names)
dump_options+=(--opt)
dump_options+=(--events)
dump_options+=(--hex-blob)
dump_options+=(--skip-comments)
dump_options+=(--fields-enclosed-by='\"')
dump_options+=(--fields-terminated-by=',')
dump_options+=(--tab="${dump_dir}")
if [ -n "${option_others}" ]; then
# word splitting is deliberate here
# shellcheck disable=SC2206
dump_options+=(${option_others})
fi
dump_options+=("${database}")
dump_cmd="mysqldump ${connect_options[*]} ${dump_options[*]}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd} 2> "${error_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: mysqldump to ${dump_dir} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_dir}"
done
}

View file

@ -0,0 +1,343 @@
#!/usr/bin/env bash
# shellcheck disable=SC2034,SC2317,SC2155
#######################################################################
# Dump a single file of all PostgreSQL databases
#
# Arguments:
# --dump-label=[String] (default: "default")
# used as suffix of the dump dir to differenciate multiple instances
# --compress=<gzip|pigz|bzip2|xz|none> (default: "gzip")
# Other options after -- are passed as-is to pg_dump
#######################################################################
dump_postgresql_global() {
local option_dump_label=""
local option_compress=""
local option_others=""
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
--dump-label)
# dump-label options, with value separated by space
if [ -n "$2" ]; then
option_dump_label="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
fi
;;
--dump-label=?*)
# dump-label options, with value separated by =
option_dump_label="${1#*=}"
;;
--dump-label=)
# dump-label options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
;;
--compress)
# compress options, with value separated by space
if [ -n "$2" ]; then
option_compress="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
exit 1
fi
;;
--compress=?*)
# compress options, with value separated by =
option_compress="${1#*=}"
;;
--compress=)
# compress options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
option_others=${*}
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
case "${option_compress}" in
none)
compress_cmd="cat"
dump_ext=""
;;
bzip2|bz|bz2)
compress_cmd="bzip2 --best"
dump_ext=".bz"
;;
xz)
compress_cmd="xz --best"
dump_ext=".xz"
;;
pigz)
compress_cmd="pigz --best"
dump_ext=".gz"
;;
gz|gzip|*)
compress_cmd="gzip --best"
dump_ext=".gz"
;;
esac
if [ -z "${option_dump_label}" ]; then
if [ -n "${option_defaults_group_suffix}" ]; then
option_dump_label="${option_defaults_group_suffix}"
elif [ -n "${option_port}" ]; then
option_dump_label="${option_port}"
elif [ -n "${option_socket}" ]; then
option_dump_label=$(path_to_str "${option_socket}")
else
option_dump_label="default"
fi
fi
local dump_dir="${LOCAL_BACKUP_DIR}/postgresql-${option_dump_label}-global"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
## example with pg_dumpall and with compression
local error_file="${errors_dir}/pg_dumpall.err"
local dump_file="${dump_dir}/pg_dumpall.sql${dump_ext}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
declare -a dump_options
dump_options=()
if [ -n "${option_others}" ]; then
# word splitting is deliberate here
# shellcheck disable=SC2206
dump_options+=(${option_others})
fi
dump_cmd="(sudo -u postgres pg_dumpall ${dump_options[*]}) 2> ${error_file} | ${compress_cmd} > ${dump_file}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd}
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: pg_dumpall to ${dump_file} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
## example with pg_dumpall and without compression
## WARNING: you need space in ~postgres
# local error_file="${errors_dir}/pg_dumpall.err"
# local dump_file="${dump_dir}/pg_dumpall.sql"
# log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
#
# (su - postgres -c "pg_dumpall > ~/pg.dump.bak") 2> "${error_file}"
# mv ~postgres/pg.dump.bak "${dump_file}"
#
# log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
}
#######################################################################
# Dump a compressed file per database
#
# Arguments: <none>
#######################################################################
dump_postgresql_per_base() {
local option_dump_label=""
local option_compress=""
local option_others=""
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
--dump-label)
# dump-label options, with value separated by space
if [ -n "$2" ]; then
option_dump_label="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
fi
;;
--dump-label=?*)
# dump-label options, with value separated by =
option_dump_label="${1#*=}"
;;
--dump-label=)
# dump-label options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
exit 1
;;
--compress)
# compress options, with value separated by space
if [ -n "$2" ]; then
option_compress="${2}"
shift
else
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
exit 1
fi
;;
--compress=?*)
# compress options, with value separated by =
option_compress="${1#*=}"
;;
--compress=)
# compress options, without value
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
option_others=${*}
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
case "${option_compress}" in
none)
compress_cmd="cat"
dump_ext=""
;;
bzip2|bz|bz2)
compress_cmd="bzip2 --best"
dump_ext=".bz"
;;
xz)
compress_cmd="xz --best"
dump_ext=".xz"
;;
pigz)
compress_cmd="pigz --best"
dump_ext=".gz"
;;
gz|gzip|*)
compress_cmd="gzip --best"
dump_ext=".gz"
;;
esac
if [ -z "${option_dump_label}" ]; then
if [ -n "${option_defaults_group_suffix}" ]; then
option_dump_label="${option_defaults_group_suffix}"
elif [ -n "${option_port}" ]; then
option_dump_label="${option_port}"
elif [ -n "${option_socket}" ]; then
option_dump_label=$(path_to_str "${option_socket}")
else
option_dump_label="default"
fi
fi
local dump_dir="${LOCAL_BACKUP_DIR}/postgresql-${option_dump_label}-per-base"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
(
# shellcheck disable=SC2164
cd /var/lib/postgresql
databases=$(sudo -u postgres psql -U postgres -lt | awk -F \| '{print $1}' | grep -v "template.*")
for database in ${databases} ; do
local error_file="${errors_dir}/${database}.err"
local dump_file="${dump_dir}/${database}.sql${dump_ext}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
declare -a dump_options
dump_options=()
dump_options+=(--create)
dump_options+=(-U postgres)
dump_options+=(-d "${database}")
if [ -n "${option_others}" ]; then
# word splitting is deliberate here
# shellcheck disable=SC2206
dump_options+=(${option_others})
fi
dump_cmd="(sudo -u postgres /usr/bin/pg_dump ${dump_options[*]}) 2> ${error_file} | ${compress_cmd} > ${dump_file}"
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
${dump_cmd}
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: pg_dump to ${dump_file} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
done
)
}
#######################################################################
# Dump a compressed file per database
#
# Arguments: <none>
#
# TODO: add arguments to include/exclude tables
#######################################################################
dump_postgresql_filtered() {
local dump_dir="${LOCAL_BACKUP_DIR}/postgresql-filtered"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
mkdir -p "${dump_dir}" "${errors_dir}"
# No need to change recursively, the top directory is enough
chmod 700 "${dump_dir}" "${errors_dir}"
local error_file="${errors_dir}/pg-backup.err"
local dump_file="${dump_dir}/pg-backup.tar"
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
## example with all tables from MYBASE excepts TABLE1 and TABLE2
# pg_dump -p 5432 -h 127.0.0.1 -U USER --clean -F t --inserts -f "${dump_file}" -t 'TABLE1' -t 'TABLE2' MYBASE 2> "${error_file}"
## example with only TABLE1 and TABLE2 from MYBASE
# pg_dump -p 5432 -h 127.0.0.1 -U USER --clean -F t --inserts -f "${dump_file}" -T 'TABLE1' -T 'TABLE2' MYBASE 2> "${error_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: pg_dump to ${dump_file} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
}

View file

@ -0,0 +1,466 @@
#!/usr/bin/env bash
# shellcheck disable=SC2034,SC2317
readonly VERSION="24.05.1"
# set all programs to C language (english)
export LC_ALL=C
# If expansion is attempted on an unset variable or parameter, the shell prints an
# error message, and, if not interactive, exits with a non-zero status.
set -o nounset
# The pipeline's return status is the value of the last (rightmost) command
# to exit with a non-zero status, or zero if all commands exit successfully.
set -o pipefail
# Enable trace mode if called with environment variable TRACE=1
if [[ "${TRACE-0}" == "1" ]]; then
set -o xtrace
fi
source "${LIBDIR}/utilities.sh"
source "${LIBDIR}/dump/elasticsearch.sh"
source "${LIBDIR}/dump/mysql.sh"
source "${LIBDIR}/dump/postgresql.sh"
source "${LIBDIR}/dump/misc.sh"
# Called from main, it is wrapping the local_tasks function defined in the real script
local_tasks_wrapper() {
log "START LOCAL_TASKS"
# Remove old log directories (recursively)
find "${LOCAL_BACKUP_DIR}/" -type d -name "${PROGNAME}.errors-*" -ctime +30 -exec rm -rf \;
local_tasks_type="$(type -t local_tasks)"
if [ "${local_tasks_type}" = "function" ]; then
local_tasks
else
log_error "There is no 'local_tasks' function to execute"
fi
# TODO: check if this is still needed
# print_error_files_content
log "STOP LOCAL_TASKS"
}
# Called from main, it is wrapping the sync_tasks function defined in the real script
sync_tasks_wrapper() {
declare -a SERVERS # Indexed array for server/port values
declare -a RSYNC_INCLUDES # Indexed array for includes
declare -a RSYNC_EXCLUDES # Indexed array for excludes
case "${SYSTEM}" in
linux)
# NOTE: remember to single-quote paths if they contain globs (*)
# and you want to defer expansion
declare -a rsync_default_includes=(
/bin
/boot
/lib
/opt
/sbin
/usr
)
;;
*bsd)
# NOTE: remember to single-quote paths if they contain globs (*)
# and you want to defer expansion
declare -a rsync_default_includes=(
/bin
/bsd
/sbin
/usr
)
;;
*)
echo "Unknown system '${SYSTEM}'" >&2
exit 1
;;
esac
if [ -f "${CANARY_FILE}" ]; then
rsync_default_includes+=("${CANARY_FILE}")
fi
readonly rsync_default_includes
# NOTE: remember to single-quote paths if they contain globs (*)
# and you want to defer expansion
declare -a rsync_default_excludes=(
/dev
/proc
/run
/sys
/tmp
/usr/doc
/usr/obj
/usr/share/doc
/usr/src
/var/apt
/var/cache
'/var/db/munin/*.tmp'
/var/lib/amavis/amavisd.sock
/var/lib/amavis/tmp
/var/lib/amavis/virusmails
'/var/lib/clamav/*.tmp'
/var/lib/elasticsearch
/var/lib/metche
/var/lib/mongodb
'/var/lib/munin/*tmp*'
/var/lib/mysql
/var/lib/php/sessions
/var/lib/php5
/var/lib/postgres
/var/lib/postgresql
/var/lib/sympa
/var/lock
/var/run
/var/spool/postfix
/var/spool/smtpd
/var/spool/squid
/var/state
/var/tmp
lost+found
'.nfs.*'
'lxc/*/rootfs/tmp'
'lxc/*/rootfs/usr/doc'
'lxc/*/rootfs/usr/obj'
'lxc/*/rootfs/usr/share/doc'
'lxc/*/rootfs/usr/src'
'lxc/*/rootfs/var/apt'
'lxc/*/rootfs/var/cache'
'lxc/*/rootfs/var/lib/php5'
'lxc/*/rootfs/var/lib/php/sessions'
'lxc/*/rootfs/var/lock'
'lxc/*/rootfs/var/run'
'lxc/*/rootfs/var/state'
'lxc/*/rootfs/var/tmp'
/home/mysqltmp
)
readonly rsync_default_excludes
sync_tasks_type="$(type -t sync_tasks)"
if [ "${sync_tasks_type}" = "function" ]; then
sync_tasks
else
log_error "There is no 'sync_tasks' function to execute"
fi
}
sync() {
local sync_name=${1}
local -a rsync_servers=("${!2}")
local -a rsync_includes=("${!3}")
local -a rsync_excludes=("${!4}")
## Initialize variable to store SSH connection errors
declare -a SSH_ERRORS=()
log "START SYNC_TASKS - sync=${sync_name}"
# echo "### sync ###"
# for server in "${rsync_servers[@]}"; do
# echo "server: ${server}"
# done
# for include in "${rsync_includes[@]}"; do
# echo "include: ${include}"
# done
# for exclude in "${rsync_excludes[@]}"; do
# echo "exclude: ${exclude}"
# done
local -i n=0
local server=""
if [ "${SERVERS_FALLBACK}" = "1" ]; then
# We try to find a suitable server
while :; do
server=$(pick_server ${n} "${sync_name}")
rc=$?
if [ ${rc} != 0 ]; then
GLOBAL_RC=${E_NOSRVAVAIL}
log "STOP SYNC_TASKS - sync=${sync_name}'"
return
fi
if test_server "${server}"; then
break
else
server=""
n=$(( n + 1 ))
fi
done
else
# we force the server
server=$(pick_server "${n}" "${sync_name}")
fi
rsync_server=$(echo "${server}" | cut -d':' -f1)
rsync_port=$(echo "${server}" | cut -d':' -f2)
log "SYNC_TASKS - sync=${sync_name}: use ${server}"
# Rsync complete log file for the current run
RSYNC_LOGFILE="/var/log/${PROGNAME}.${sync_name}.rsync.log"
# Rsync stats for the current run
RSYNC_STATSFILE="/var/log/${PROGNAME}.${sync_name}.rsync-stats.log"
# reset Rsync log file
if [ -n "$(command -v truncate)" ]; then
truncate -s 0 "${RSYNC_LOGFILE}"
truncate -s 0 "${RSYNC_STATSFILE}"
else
printf "" > "${RSYNC_LOGFILE}"
printf "" > "${RSYNC_STATSFILE}"
fi
# Initialize variable here, we need it later
local -a mtree_files=()
if [ "${MTREE_ENABLED}" = "1" ]; then
mtree_bin=$(command -v mtree)
if [ -n "${mtree_bin}" ]; then
# Dump filesystem stats with mtree
log "SYNC_TASKS - sync=${sync_name}: start mtree"
# Loop over Rsync includes
for i in "${!rsync_includes[@]}"; do
include="${rsync_includes[i]}"
if [ -d "${include}" ]; then
# … but exclude for mtree what will be excluded by Rsync
mtree_excludes_file="$(mktemp --tmpdir "${PROGNAME}.${sync_name}.mtree-excludes.XXXXXX")"
add_to_temp_files "${mtree_excludes_file}"
for j in "${!rsync_excludes[@]}"; do
echo "${rsync_excludes[j]}" | grep -E "^([^/]|${include})" | sed -e "s|^${include}|.|" >> "${mtree_excludes_file}"
done
mtree_file="/var/log/evobackup.$(basename "${include}").mtree"
add_to_temp_files "${mtree_file}"
${mtree_bin} -x -c -p "${include}" -X "${mtree_excludes_file}" > "${mtree_file}"
mtree_files+=("${mtree_file}")
fi
done
if [ "${#mtree_files[@]}" -le 0 ]; then
log_error "SYNC_TASKS - ${sync_name}: ERROR: mtree didn't produce any file"
fi
log "SYNC_TASKS - sync=${sync_name}: stop mtree (files: ${mtree_files[*]})"
else
log "SYNC_TASKS - sync=${sync_name}: skip mtree (missing)"
fi
else
log "SYNC_TASKS - sync=${sync_name}: skip mtree (disabled)"
fi
rsync_bin=$(command -v rsync)
# Build the final Rsync command
# Rsync main options
rsync_main_args=()
rsync_main_args+=(--archive)
rsync_main_args+=(--itemize-changes)
rsync_main_args+=(--quiet)
rsync_main_args+=(--stats)
rsync_main_args+=(--human-readable)
rsync_main_args+=(--relative)
rsync_main_args+=(--partial)
rsync_main_args+=(--delete)
rsync_main_args+=(--delete-excluded)
rsync_main_args+=(--force)
rsync_main_args+=(--ignore-errors)
rsync_main_args+=(--log-file "${RSYNC_LOGFILE}")
rsync_main_args+=(--rsh "ssh -p ${rsync_port} -o 'ConnectTimeout ${SSH_CONNECT_TIMEOUT}'")
# Rsync excludes
for i in "${!rsync_excludes[@]}"; do
rsync_main_args+=(--exclude "${rsync_excludes[i]}")
done
# Rsync local sources
rsync_main_args+=("${rsync_includes[@]}")
# Rsync remote destination
rsync_main_args+=("root@${rsync_server}:${REMOTE_BACKUP_DIR}/")
# … log it
log "SYNC_TASKS - sync=${sync_name}: Rsync main command : ${rsync_bin} ${rsync_main_args[*]}"
# … execute it
${rsync_bin} "${rsync_main_args[@]}"
rsync_main_rc=$?
# Copy last lines of rsync log to the main log
tail -n 30 "${RSYNC_LOGFILE}" >> "${LOGFILE}"
# Copy Rsync stats to special file
tail -n 30 "${RSYNC_LOGFILE}" | grep --invert-match --extended-regexp " [\<\>ch\.\*]\S{10} " > "${RSYNC_STATSFILE}"
# We ignore rc=24 (vanished files)
if [ ${rsync_main_rc} -ne 0 ] && [ ${rsync_main_rc} -ne 24 ]; then
log_error "SYNC_TASKS - sync=${sync_name}: Rsync main command returned an error ${rsync_main_rc}" "${LOGFILE}"
GLOBAL_RC=${E_SYNCFAILED}
else
# Build the report Rsync command
local -a rsync_report_args
rsync_report_args=()
# Rsync options
rsync_report_args+=(--rsh "ssh -p ${rsync_port} -o 'ConnectTimeout ${SSH_CONNECT_TIMEOUT}'")
# Rsync local sources
if [ "${#mtree_files[@]}" -gt 0 ]; then
# send mtree files if there is any
rsync_report_args+=("${mtree_files[@]}")
fi
if [ -f "${RSYNC_LOGFILE}" ]; then
# send rsync full log file if it exists
rsync_report_args+=("${RSYNC_LOGFILE}")
fi
if [ -f "${RSYNC_STATSFILE}" ]; then
# send rsync stats log file if it exists
rsync_report_args+=("${RSYNC_STATSFILE}")
fi
# Rsync remote destination
rsync_report_args+=("root@${rsync_server}:${REMOTE_LOG_DIR}/")
# … log it
log "SYNC_TASKS - sync=${sync_name}: Rsync report command : ${rsync_bin} ${rsync_report_args[*]}"
# … execute it
${rsync_bin} "${rsync_report_args[@]}"
fi
log "STOP SYNC_TASKS - sync=${sync_name}"
}
setup() {
# Default return-code (0 == succes)
GLOBAL_RC=0
# Possible error codes
readonly E_NOSRVAVAIL=21 # No server is available
readonly E_SYNCFAILED=20 # Failed sync task
readonly E_DUMPFAILED=10 # Failed dump task
# explicit PATH
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin
# System name (linux, openbsd…)
: "${SYSTEM:=$(uname | tr '[:upper:]' '[:lower:]')}"
# Hostname (for logs and notifications)
: "${HOSTNAME:=$(hostname)}"
# Store pid in a file named after this program's name
: "${PROGNAME:=$(basename "$0")}"
: "${PIDFILE:="/var/run/${PROGNAME}.pid"}"
# Customize the log path if you want multiple scripts to have separate log files
: "${LOGFILE:="/var/log/evobackup.log"}"
# Canary file to update before executing tasks
: "${CANARY_FILE:="/zzz_evobackup_canary"}"
# Date format for log messages
: "${DATE_FORMAT:="%Y-%m-%d %H:%M:%S"}"
# Should we fallback on other servers when the first one is unreachable?
: "${SERVERS_FALLBACK:=1}"
# timeout (in seconds) for SSH connections
: "${SSH_CONNECT_TIMEOUT:=90}"
: "${LOCAL_BACKUP_DIR:="/home/backup"}"
# shellcheck disable=SC2174
mkdir -p -m 700 "${LOCAL_BACKUP_DIR}"
: "${ERRORS_DIR:="${LOCAL_BACKUP_DIR}/${PROGNAME}.errors-${START_TIME}"}"
# shellcheck disable=SC2174
mkdir -p -m 700 "${ERRORS_DIR}"
# Backup directory on remote server
: "${REMOTE_BACKUP_DIR:="/var/backup"}"
# Log directory in remote server
: "${REMOTE_LOG_DIR:="/var/log"}"
# Email address for notifications
: "${MAIL:="root"}"
# Email subject for notifications
: "${MAIL_SUBJECT:="[info] EvoBackup - Client ${HOSTNAME}"}"
# Enable/disable local tasks (default: enabled)
: "${LOCAL_TASKS:=1}"
# Enable/disable sync tasks (default: enabled)
: "${SYNC_TASKS:=1}"
# Enable/disable mtree (default: enabled)
: "${MTREE_ENABLED:=1}"
# If "setup_custom" exists and is a function, let's call it
setup_custom_type="$(type -t setup_custom)"
if [ "${setup_custom_type}" = "function" ]; then
setup_custom
fi
## Force umask
umask 077
# Initialize a list of temporary files
declare -a TEMP_FILES=()
# Any file in this list will be deleted when the program exits
trap "cleanup" EXIT
}
run_evobackup() {
# Start timer
START_EPOCH=$(/bin/date +%s)
START_TIME=$(/bin/date +"%Y%m%d%H%M%S")
# Configure variables and environment
setup
log "START GLOBAL - VERSION=${VERSION} LOCAL_TASKS=${LOCAL_TASKS} SYNC_TASKS=${SYNC_TASKS}"
# /!\ Only one backup processus can run at the sametime /!\
# Based on PID file, kill any running process before continuing
enforce_single_process "${PIDFILE}"
# Update canary to keep track of each run
update-evobackup-canary --who "${PROGNAME}" --file "${CANARY_FILE}"
if [ "${LOCAL_TASKS}" = "1" ]; then
local_tasks_wrapper
fi
if [ "${SYNC_TASKS}" = "1" ]; then
sync_tasks_wrapper
fi
STOP_EPOCH=$(/bin/date +%s)
case "${SYSTEM}" in
*bsd)
start_time=$(/bin/date -f "%s" -j "${START_EPOCH}" +"${DATE_FORMAT}")
stop_time=$(/bin/date -f "%s" -j "${STOP_EPOCH}" +"${DATE_FORMAT}")
;;
*)
start_time=$(/bin/date --date="@${START_EPOCH}" +"${DATE_FORMAT}")
stop_time=$(/bin/date --date="@${STOP_EPOCH}" +"${DATE_FORMAT}")
;;
esac
duration=$(( STOP_EPOCH - START_EPOCH ))
log "STOP GLOBAL - start='${start_time}' stop='${stop_time}' duration=${duration}s"
send_mail
exit ${GLOBAL_RC}
}

View file

@ -0,0 +1,143 @@
#!/usr/bin/env bash
# Output a message to the log file
log() {
local msg="${1:-$(cat /dev/stdin)}"
local pid=$$
printf "[%s] %s[%s]: %s\\n" \
"$(/bin/date +"${DATE_FORMAT}")" "${PROGNAME}" "${pid}" "${msg}" \
>> "${LOGFILE}"
}
log_error() {
local error_msg=${1}
local error_file=${2:-""}
if [ -n "${error_file}" ] && [ -f "${error_file}" ]; then
printf "\n### %s\n" "${error_msg}" >&2
# shellcheck disable=SC2046
if [ $(wc -l "${error_file}" | cut -d " " -f 1) -gt 30 ]; then
printf "~~~{%s (tail -30)}\n" "${error_file}" >&2
tail -n 30 "${error_file}" >&2
else
printf "~~~{%s}\n" "${error_file}" >&2
cat "${error_file}" >&2
fi
printf "~~~\n" >&2
log "${error_msg}, check ${error_file}"
else
printf "\n### %s\n" "${error_msg}" >&2
log "${error_msg}"
fi
}
add_to_temp_files() {
TEMP_FILES+=("${1}")
}
# Remove all temporary file created during the execution
cleanup() {
# shellcheck disable=SC2086
rm -f "${TEMP_FILES[@]}"
find "${ERRORS_DIR}" -type d -empty -delete
}
enforce_single_process() {
local pidfile=$1
if [ -e "${pidfile}" ]; then
pid=$(cat "${pidfile}")
# Does process still exist?
if kill -0 "${pid}" 2> /dev/null; then
# Killing the childs of evobackup.
for ppid in $(pgrep -P "${pid}"); do
kill -9 "${ppid}";
done
# Then kill the main PID.
kill -9 "${pid}"
printf "%s is still running (PID %s). Process has been killed" "$0" "${pid}\\n" >&2
else
rm -f "${pidfile}"
fi
fi
add_to_temp_files "${pidfile}"
echo "$$" > "${pidfile}"
}
# Build the error directory (inside ERRORS_DIR) based on the dump directory path
errors_dir_from_dump_dir() {
local dump_dir=$1
local relative_path=$(realpath --relative-to="${LOCAL_BACKUP_DIR}" "${dump_dir}")
# return absolute path
realpath --canonicalize-missing "${ERRORS_DIR}/${relative_path}"
}
# Call test_server with "HOST:PORT" string
# It will return with 0 if the server is reachable.
# It will return with 1 and a message on stderr if not.
test_server() {
local item=$1
# split HOST and PORT from the input string
local host=$(echo "${item}" | cut -d':' -f1)
local port=$(echo "${item}" | cut -d':' -f2)
local new_error
# Test if the server is accepting connections
ssh -q -o "ConnectTimeout ${SSH_CONNECT_TIMEOUT}" "${host}" -p "${port}" -t "exit"
# shellcheck disable=SC2181
if [ $? = 0 ]; then
# SSH connection is OK
return 0
else
# SSH connection failed
new_error=$(printf "Failed to connect to \`%s' within %s seconds" "${item}" "${SSH_CONNECT_TIMEOUT}")
log "${new_error}"
SSH_ERRORS+=("${new_error}")
return 1
fi
}
# Call pick_server with an optional positive integer to get the nth server in the list.
pick_server() {
local -i increment=${1:-0}
local -i list_length=${#SERVERS[@]}
local sync_name=${2:-""}
if (( increment >= list_length )); then
# We've reached the end of the list
new_error="No more server available"
new_error="${new_error} for sync '${sync_name}'"
log "${new_error}"
SSH_ERRORS+=("${new_error}")
# Log errors to stderr
for i in "${!SSH_ERRORS[@]}"; do
printf "%s\n" "${SSH_ERRORS[i]}" >&2
done
return 1
fi
# Extract the day of month, without leading 0 (which would give an octal based number)
today=$(/bin/date +%e)
# A salt is useful to randomize the starting point in the list
# but stay identical each time it's called for a server (based on hostname).
salt=$(hostname | cksum | cut -d' ' -f1)
# Pick an integer between 0 and the length of the SERVERS list
# It changes each day
n=$(( (today + salt + increment) % list_length ))
echo "${SERVERS[n]}"
}
send_mail() {
tail -20 "${LOGFILE}" | mail -s "${MAIL_SUBJECT}" "${MAIL}"
}
path_to_str() {
echo "${1}" | sed -e 's|^/||; s|/$||; s|/|:|g'
}

View file

@ -0,0 +1,326 @@
#!/usr/bin/env bash
#
# Evobackup client
# See https://gitea.evolix.org/evolix/evobackup
#
# This is a generated backup script made by:
# command: @COMMAND@
# version: @VERSION@
# date: @DATE@
#######################################################################
#
# You must configure the MAIL variable to receive notifications.
#
# There is some optional configuration that you can do
# at the end of this script.
#
#######################################################################
# Email adress for notifications
MAIL=__NOTIFICATION_MAIL__
#######################################################################
#
# The "sync_tasks" function will be called by the "run_evobackup" function.
#
# You can customize the variables:
# * "SYNC_NAME" (String)
# * "SERVERS" (Array of HOST:PORT)
# * "RSYNC_INCLUDES" (Array of paths to include)
# * "RSYNC_EXCLUDES" (Array of paths to exclude)
#
# WARNING: remember to single-quote paths if they contain globs (*)
# and you want to pass them as-is to Rsync.
#
# The "sync" function can be called multiple times
# with a different set of variables.
# That way you can to sync to various destinations.
#
# Default includes/excludes are defined in the "main" library,
# referenced at this end of this file.
#
#######################################################################
# shellcheck disable=SC2034
sync_tasks() {
########## System-only backup (to Evolix servers) #################
SYNC_NAME="evolix-system"
SERVERS=(
__SRV0_HOST__:__SRV0_PORT__
__SRV1_HOST__:__SRV1_PORT__
)
RSYNC_INCLUDES=(
"${rsync_default_includes[@]}"
/etc
/root
/var
)
RSYNC_EXCLUDES=(
"${rsync_default_excludes[@]}"
)
sync "${SYNC_NAME}" "SERVERS[@]" "RSYNC_INCLUDES[@]" "RSYNC_EXCLUDES[@]"
########## Full backup (to client servers) ########################
### SYNC_NAME="client-full"
### SERVERS=(
### client-backup00.evolix.net:2221
### client-backup01.evolix.net:2221
### )
### RSYNC_INCLUDES=(
### "${rsync_default_includes[@]}"
### /etc
### /root
### /var
### /home
### /srv
### )
### RSYNC_EXCLUDES=(
### "${rsync_default_excludes[@]}"
### )
### sync "${SYNC_NAME}" "SERVERS[@]" "RSYNC_INCLUDES[@]" "RSYNC_EXCLUDES[@]"
}
#######################################################################
#
# The "local_tasks" function will be called by the "run_evobackup" function.
#
# You can call any available "dump_xxx" function
# (usually installed at /usr/local/lib/evobackup/dump-*.sh)
#
# You can also write some custom functions and call them.
# A "dump_custom" example is available further down.
#
#######################################################################
local_tasks() {
########## Server state ###########
# Run dump-server-state to extract system information
#
# Options : any dump-server-state supported option
# (except --dump-dir that will be overwritten)
# See 'dump-server-state -h' for details.
#
dump_server_state
########## MySQL ##################
# Very common strategy for a single instance server with default configuration :
#
### dump_mysql_global; dump_mysql_grants; dump_mysql_summary
#
# See below for details regarding dump functions for MySQL/MariaDB
# Dump all databases in a single compressed file
#
# Options :
# --masterdata (default: <absent>)
# --port=[Integer] (default: <blank>)
# --socket=[String] (default: <blank>)
# --user=[String] (default: <blank>)
# --password=[String] (default: <blank>)
# --defaults-file=[String] (default: <blank>)
# --defaults-extra-file=[String] (default: <blank>)
# --defaults-group-suffix=[String] (default: <blank>)
# --dump-label=[String] (default: "default")
# used as suffix of the dump dir to differenciate multiple instances
#
### dump_mysql_global
# Dump each database separately, in a compressed file
#
# Options :
# --port=[Integer] (default: <blank>)
# --socket=[String] (default: <blank>)
# --user=[String] (default: <blank>)
# --password=[String] (default: <blank>)
# --defaults-file=[String] (default: <blank>)
# --defaults-extra-file=[String] (default: <blank>)
# --defaults-group-suffix=[String] (default: <blank>)
# --dump-label=[String] (default: "default")
# used as suffix of the dump dir to differenciate multiple instances
#
### dump_mysql_per_base
# Dump permissions of an instance (using pt-show-grants)
#
# Options :
# --port=[Integer] (default: <blank>)
# --socket=[String] (default: <blank>)
# --user=[String] (default: <blank>)
# --password=[String] (default: <blank>)
# --defaults-file=[String] (default: <blank>)
# --dump-label=[String] (default: "default")
# used as suffix of the dump dir to differenciate multiple instances
#
# WARNING - unsupported options :
# --defaults-extra-file
# --defaults-group-suffix
# You have to provide credentials manually
#
### dump_mysql_grants
# Dump complete summary of an instance (using pt-mysql-summary)
#
# Options :
# --port=[Integer] (default: <blank>)
# --socket=[String] (default: <blank>)
# --user=[String] (default: <blank>)
# --password=[String] (default: <blank>)
# --defaults-file=[String] (default: <blank>)
# --defaults-extra-file=[String] (default: <blank>)
# --defaults-group-suffix=[String] (default: <blank>)
# --dump-label=[String] (default: "default")
# used as suffix of the dump dir to differenciate multiple instances
#
### dump_mysql_summary
# Dump each table in separate schema/data files
#
# Options :
# --port=[Integer] (default: <blank>)
# --socket=[String] (default: <blank>)
# --user=[String] (default: <blank>)
# --password=[String] (default: <blank>)
# --defaults-file=[String] (default: <blank>)
# --defaults-extra-file=[String] (default: <blank>)
# --defaults-group-suffix=[String] (default: <blank>)
# --dump-label=[String] (default: "default")
# used as suffix of the dump dir to differenciate multiple instances
#
### dump_mysql_tabs
########## PostgreSQL #############
# Dump all databases in a single file (compressed or not)
#
### dump_postgresql_global
# Dump a specific databse with only some tables, or all but some tables (must be configured)
#
### dump_postgresql_filtered
# Dump each database separately, in a compressed file
#
### dump_postgresql_per_base
########## MongoDB ################
### dump_mongodb [--user=foo] [--password=123456789]
########## Redis ##################
# Copy data file for all instances
#
### dump_redis [--instances=<all|instance1|instance2>]
########## Elasticsearch ##########
# Snapshot data for a single-node cluster
#
### dump_elasticsearch_snapshot_singlenode [--protocol=http] [--host=localhost] [--port=9200] [--user=foo] [--password=123456789] [--repository=snaprepo] [--snapshot=snapshot.daily]
# Snapshot data for a multi-node cluster
#
### dump_elasticsearch_snapshot_multinode [--protocol=http] [--host=localhost] [--port=9200] [--user=foo] [--password=123456789] [--repository=snaprepo] [--snapshot=snapshot.daily] [--nfs-server=192.168.2.1]
########## RabbitMQ ###############
### dump_rabbitmq
########## MegaCli ################
# Copy RAID config
#
### dump_megacli_config
# Dump file access control lists
#
### dump_facl
########## OpenLDAP ###############
### dump_ldap
########## Network ################
# Dump network routes with mtr and traceroute (warning: could be long with aggressive firewalls)
#
### dump_traceroute --targets=host_or_ip[,host_or_ip]
dump_traceroute --targets=8.8.8.8,www.evolix.fr,travaux.evolix.net
# No-op, in case nothing is enabled
:
}
# This is an example for a custom dump function
# Uncomment, customize and call it from the "local_tasks" function
### dump_custom() {
### # Set dump and errors directories and files
### local dump_dir="${LOCAL_BACKUP_DIR}/custom"
### local dump_file="${dump_dir}/dump.gz"
### local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
### local error_file="${errors_dir}/dump.err"
###
### # Reset dump and errors directories
### rm -rf "${dump_dir}" "${errors_dir}"
### # shellcheck disable=SC2174
### mkdir -p -m 700 "${dump_dir}" "${errors_dir}"
###
### # Log the start of the function
### log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
###
### # Prepare the dump command (errors go to the error file and the data to the dump file)
### dump_cmd="my-dump-command 2> ${error_file} > ${dump_file}"
### log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
###
### # Execute the dump command
### ${dump_cmd}
###
### # Check result and deal with potential errors
### local last_rc=$?
### # shellcheck disable=SC2086
### if [ ${last_rc} -ne 0 ]; then
### log_error "LOCAL_TASKS - ${FUNCNAME[0]}: my-dump-command to ${dump_file} returned an error ${last_rc}" "${error_file}"
### GLOBAL_RC=${E_DUMPFAILED}
### else
### rm -f "${error_file}"
### fi
###
### # Log the end of the function
### log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
### }
########## Optional configuration #####################################
setup_custom() {
# System name ("linux" and "openbsd" currently supported)
### SYSTEM="$(uname)"
# Host name for logs and notifications
### HOSTNAME="$(hostname)"
# Email subject for notifications
### MAIL_SUBJECT="[info] EvoBackup - Client ${HOSTNAME}"
# No-op in case nothing is executed
:
}
########## Libraries ##################################################
# Change this to wherever you install the libraries
LIBDIR="/usr/local/lib/evobackup"
source "${LIBDIR}/main.sh"
########## Let's go! ##################################################
run_evobackup

View file

@ -0,0 +1,50 @@
---
- name: Dependencies are present
ansible.builtin.apt:
name:
- rsync
- mtree-netbsd
state: present
- name: "Remount /usr if needed"
include_role:
name: remount-usr
when: evobackup_client__lib_dir is search("/usr") or evobackup_client__bin_dir is search("/usr")
- name: copy evobackup libs
ansible.builtin.copy:
src: upstream/lib/
dest: "{{ evobackup_client__lib_dir }}/"
force: True
mode: "0644"
owner: root
group: root
- name: copy evobackupctl script
ansible.builtin.copy:
src: upstream/bin/evobackupctl
dest: "{{ evobackup_client__bin_dir }}/evobackupctl"
force: True
mode: "0755"
owner: root
group: root
- name: LIBDIR is customized in evobackupctl
ansible.builtin.replace:
path: "{{ evobackup_client__bin_dir }}/evobackupctl"
regexp: "^LIBDIR=.+"
replace: "LIBDIR=\"{{ evobackup_client__lib_dir }}\""
- name: Evobackup canary cron is present
ansible.builtin.template:
src: update-evobackup-canary.sh.j2
dest: "{{ evobackup_client__update_canary_path }}"
mode: "0700"
when: evobackup_client__update_canary_enable | bool
- name: Evobackup canary cron is absent
ansible.builtin.file:
path: "{{ evobackup_client__update_canary_path }}"
state: absent
when: not ( evobackup_client__update_canary_enable | bool)

View file

@ -1,26 +1,31 @@
---
- ansible.builtin.include: "ssh_key.yml"
tags:
- evobackup_client
- evobackup_client_backup_ssh_key
- name: Install evobackup client components
ansible.builtin.include: "install.yml"
- ansible.builtin.include: "jail.yml"
tags:
- evobackup_client
- evobackup_client_jail
### This is commented because supposedly non-functionnal
- ansible.builtin.include: "upload_scripts.yml"
tags:
- evobackup_client
- evobackup_client_backup_scripts
# - ansible.builtin.include: "ssh_key.yml"
# tags:
# - evobackup_client
# - evobackup_client_backup_ssh_key
- ansible.builtin.include: "open_ssh_ports.yml"
tags:
- evobackup_client
- evobackup_client_backup_firewall
# - ansible.builtin.include: "jail.yml"
# tags:
# - evobackup_client
# - evobackup_client_jail
- ansible.builtin.include: "verify_ssh.yml"
tags:
- evobackup_client
- evobackup_client_backup_hosts
# - ansible.builtin.include: "upload_scripts.yml"
# tags:
# - evobackup_client
# - evobackup_client_backup_scripts
# - ansible.builtin.include: "open_ssh_ports.yml"
# tags:
# - evobackup_client
# - evobackup_client_backup_firewall
# - ansible.builtin.include: "verify_ssh.yml"
# tags:
# - evobackup_client
# - evobackup_client_backup_hosts

View file

@ -0,0 +1,3 @@
#!/bin/sh
update-evobackup-canary --who {{ evobackup_client__update_canary_who | mandatory }}

View file

@ -1,305 +0,0 @@
#!/bin/sh
# Careful, the zzz_evobackup template was last updated on 2020/06/08
#
# Script Evobackup client
# See https://gitea.evolix.org/evolix/evobackup
#
# Author: Gregory Colpart <reg@evolix.fr>
# Contributors:
# Romain Dessort <rdessort@evolix.fr>
# Benoît Série <bserie@evolix.fr>
# Tristan Pilat <tpilat@evolix.fr>
# Victor Laborie <vlaborie@evolix.fr>
# Jérémy Lecour <jlecour@evolix.fr>
#
# Licence: AGPLv3
#
# /!\ DON'T FORGET TO SET "MAIL" and "SERVERS" VARIABLES
# Fail on unassigned variables
set -u
##### Configuration ###################################################
# email adress for notifications
MAIL={{ evobackup_client__mail }}
# list of hosts (hostname or IP) and SSH port for Rsync
SERVERS="{% for host in evobackup_client__hosts %}{{ host.name }}:{{ host.port }}{% if loop.index != loop.length %} {% endif %}{% endfor %}"
# Should we fallback on servers when the first is unreachable ?
SERVERS_FALLBACK={{ evobackup_client__servers_fallback }}
# timeout (in seconds) for SSH connections
SSH_CONNECT_TIMEOUT=${SSH_CONNECT_TIMEOUT:-30}
## We use /home/backup : feel free to use your own dir
LOCAL_BACKUP_DIR="{{ evobackup_client__backup_path }}"
# You can set "linux" or "bsd" manually or let it choose automatically
SYSTEM=$(uname | tr '[:upper:]' '[:lower:]')
# Change these 2 variables if you have more than one backup cron
PIDFILE="{{ evobackup_client__pid_path }}"
LOGFILE="{{ evobackup_client__log_path }}"
## Enable/Disable tasks
LOCAL_TASKS=${LOCAL_TASKS:-1}
SYNC_TASKS=${SYNC_TASKS:-1}
##### SETUP AND FUNCTIONS #############################################
BEGINNING=$(/bin/date +"%d-%m-%Y ; %H:%M")
# shellcheck disable=SC2174
mkdir -p -m 700 ${LOCAL_BACKUP_DIR}
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin
## lang = C for english outputs
export LANGUAGE=C
export LANG=C
## Force umask
umask 077
## Initialize variable to store SSH connection errors
SERVERS_SSH_ERRORS=""
# Call test_server with "HOST:PORT" string
# It will return with 0 if the server is reachable.
# It will return with 1 and a message on stderr if not.
test_server() {
item=$1
# split HOST and PORT from the input string
host=$(echo "${item}" | cut -d':' -f1)
port=$(echo "${item}" | cut -d':' -f2)
# Test if the server is accepting connections
ssh -q -o "ConnectTimeout ${SSH_CONNECT_TIMEOUT}" -i {{ evobackup_client__root_key_path }} "${host}" -p "${port}" -t "exit"
# shellcheck disable=SC2181
if [ $? = 0 ]; then
# SSH connection is OK
return 0
else
# SSH connection failed
new_error=$(printf "Failed to connect to \`%s' within %s seconds" "${item}" "${SSH_CONNECT_TIMEOUT}")
SERVERS_SSH_ERRORS=$(printf "%s\\n%s" "${SERVERS_SSH_ERRORS}" "${new_error}" | sed -e '/^$/d')
return 1
fi
}
# Call pick_server with an optional positive integer to get the nth server in the list.
pick_server() {
increment=${1:-0}
list_length=$(echo "${SERVERS}" | wc -w)
if [ "${increment}" -ge "${list_length}" ]; then
# We've reached the end of the list
new_error="No more server available"
SERVERS_SSH_ERRORS=$(printf "%s\\n%s" "${SERVERS_SSH_ERRORS}" "${new_error}" | sed -e '/^$/d')
# Log errors to stderr
printf "%s\\n" "${SERVERS_SSH_ERRORS}" >&2
# Log errors to logfile
printf "%s\\n" "${SERVERS_SSH_ERRORS}" >> $LOGFILE
return 1
fi
# Extract the day of month, without leading 0 (which would give an octal based number)
today=$(date +%e)
# A salt is useful to randomize the starting point in the list
# but stay identical each time it's called for a server (based on hostname).
salt=$(hostname | cksum | cut -d' ' -f1)
# Pick an integer between 0 and the length of the SERVERS list
# It changes each day
item=$(( (today + salt + increment) % list_length ))
# cut starts counting fields at 1, not 0.
field=$(( item + 1 ))
echo "${SERVERS}" | cut -d' ' -f${field}
}
## Verify other evobackup process and kill if needed
if [ -e "${PIDFILE}" ]; then
pid=$(cat "${PIDFILE}")
# Does process still exist ?
if kill -0 "${pid}" 2>/dev/null; then
# Killing the childs of evobackup.
for ppid in $(pgrep -P "${pid}"); do
kill -9 "${ppid}";
done
# Then kill the main PID.
kill -9 "${pid}"
printf "%s is still running (PID %s). Process has been killed" "$0" "${pid}\\n" >&2
else
rm -f ${PIDFILE}
fi
fi
echo "$$" > ${PIDFILE}
# shellcheck disable=SC2064
trap "rm -f ${PIDFILE}" EXIT
##### LOCAL BACKUP ####################################################
if [ "${LOCAL_TASKS}" = "1" ]; then
## Dump system and kernel versions
uname -a > ${LOCAL_BACKUP_DIR}/uname
## Dump network routes with mtr and traceroute (warning: could be long with aggressive firewalls)
for addr in 8.8.8.8 www.evolix.fr travaux.evolix.net; do
mtr -r ${addr} > ${LOCAL_BACKUP_DIR}/mtr-${addr}
traceroute -n ${addr} > ${LOCAL_BACKUP_DIR}/traceroute-${addr} 2>&1
done
## Dump process with ps
ps auwwx >${LOCAL_BACKUP_DIR}/ps.out
if [ "${SYSTEM}" = "linux" ]; then
## Dump network connections with ss
ss -taupen > ${LOCAL_BACKUP_DIR}/netstat.out
## List Debian packages
dpkg -l > ${LOCAL_BACKUP_DIR}/packages
dpkg --get-selections > ${LOCAL_BACKUP_DIR}/packages.getselections
apt-cache dumpavail > ${LOCAL_BACKUP_DIR}/packages.available
## Dump MBR / table partitions
disks=$(lsblk -l | grep disk | grep -v -E '(drbd|fd[0-9]+)' | awk '{print $1}')
for disk in ${disks}; do
dd if="/dev/${disk}" of="${LOCAL_BACKUP_DIR}/MBR-${disk}" bs=512 count=1 2>&1 | grep -Ev "(records in|records out|512 bytes)"
fdisk -l "/dev/${disk}" > "${LOCAL_BACKUP_DIR}/partitions-${disk}" 2>&1
done
cat ${LOCAL_BACKUP_DIR}/partitions-* > ${LOCAL_BACKUP_DIR}/partitions
## Dump iptables
if [ -x /sbin/iptables ]; then
{ /sbin/iptables -L -n -v; /sbin/iptables -t filter -L -n -v; } > ${LOCAL_BACKUP_DIR}/iptables.txt
fi
## Dump findmnt(8) output
FINDMNT_BIN=$(command -v findmnt)
if [ -x "${FINDMNT_BIN}" ]; then
${FINDMNT_BIN} > ${LOCAL_BACKUP_DIR}/findmnt.txt
fi
else
## Dump network connections with netstat
netstat -finet -atn > ${LOCAL_BACKUP_DIR}/netstat.out
## List OpenBSD packages
pkg_info -m > ${LOCAL_BACKUP_DIR}/packages
## Dump MBR / table partitions
disklabel sd0 > ${LOCAL_BACKUP_DIR}/partitions
## Dump pf infos
pfctl -sa > ${LOCAL_BACKUP_DIR}/pfctl-sa.txt
fi
## Dump rights
#getfacl -R /var > ${LOCAL_BACKUP_DIR}/rights-var.txt
#getfacl -R /etc > ${LOCAL_BACKUP_DIR}/rights-etc.txt
#getfacl -R /usr > ${LOCAL_BACKUP_DIR}/rights-usr.txt
#getfacl -R /home > ${LOCAL_BACKUP_DIR}/rights-home.txt
fi
##### REMOTE BACKUP ###################################################
n=0
server=""
if [ "${SERVERS_FALLBACK}" = "1" ]; then
# We try to find a suitable server
while :; do
server=$(pick_server "${n}")
test $? = 0 || exit 2
if test_server "${server}"; then
break
else
server=""
n=$(( n + 1 ))
fi
done
else
# we force the server
server=$(pick_server "${n}")
fi
SSH_SERVER=$(echo "${server}" | cut -d':' -f1)
SSH_PORT=$(echo "${server}" | cut -d':' -f2)
HOSTNAME=$(hostname)
if [ "${SYSTEM}" = "linux" ]; then
rep="/bin /boot /lib /opt /sbin /usr /srv"
else
rep="/bsd /bin /sbin /usr"
fi
if [ "${SYNC_TASKS}" = "1" ]; then
# /!\ DO NOT USE COMMENTS in the rsync command /!\
# It breaks the command and destroys data, simply remove (or add) lines.
# Remote shell command
RSH_COMMAND="ssh -i {{ evobackup_client__root_key_path }} -p ${SSH_PORT} -o 'ConnectTimeout ${SSH_CONNECT_TIMEOUT}'"
# ignore check because we want it to split the different arguments to $rep
# shellcheck disable=SC2086
rsync -avzh --stats --delete --delete-excluded --force --ignore-errors --partial \
--exclude "lost+found" \
--exclude ".nfs.*" \
--exclude "/var/log" \
--exclude "/var/log/evobackup*" \
--exclude "/var/lib/mysql" \
--exclude "/var/lib/postgres" \
--exclude "/var/lib/postgresql" \
--exclude "/var/lib/sympa" \
--exclude "/var/lib/metche" \
--exclude "/var/run" \
--exclude "/var/lock" \
--exclude "/var/state" \
--exclude "/var/apt" \
--exclude "/var/cache" \
--exclude "/usr/src" \
--exclude "/usr/doc" \
--exclude "/usr/share/doc" \
--exclude "/usr/obj" \
--exclude "dev" \
--exclude "/var/spool/postfix" \
--exclude "/var/lib/amavis/amavisd.sock" \
--exclude "/var/lib/munin/*tmp*" \
--exclude "/var/lib/php5" \
--exclude "/var/spool/squid" \
--exclude "/var/lib/elasticsearch" \
--exclude "/var/lib/amavis/tmp" \
--exclude "/var/lib/clamav/*.tmp" \
--exclude "/home/mysqltmp" \
--exclude "/var/lib/php/sessions" \
${rep} \
/etc \
/root \
/var \
-e "${RSH_COMMAND}" \
"root@${SSH_SERVER}:/var/backup/" \
| tail -30 >> $LOGFILE
fi
##### REPORTING #######################################################
END=$(/bin/date +"%d-%m-%Y ; %H:%M")
printf "EvoBackup - %s - START %s ON %s (LOCAL_TASKS=%s SYNC_TASKS=%s)\\n" \
"${HOSTNAME}" "${BEGINNING}" "${SSH_SERVER}" "${LOCAL_TASKS}" "${SYNC_TASKS}" \
>> $LOGFILE
printf "EvoBackup - %s - STOP %s ON %s (LOCAL_TASKS=%s SYNC_TASKS=%s)\\n" \
"${HOSTNAME}" "${END}" "${SSH_SERVER}" "${LOCAL_TASKS}" "${SYNC_TASKS}" \
>> $LOGFILE
tail -10 $LOGFILE | \
mail -s "[info] EvoBackup - Client ${HOSTNAME}" \
${MAIL}

View file

@ -4,7 +4,7 @@
# Script to verify compliance of a Linux (Debian) server
# powered by Evolix
VERSION="23.11.1"
VERSION="24.01"
readonly VERSION
# base functions

Some files were not shown because too many files have changed in this diff Show more