forked from evolix/ansible-roles
evobackup-client: replace non-functional role with install tasks
This commit is contained in:
parent
4c2548b21d
commit
e5bbb601d3
|
@ -16,6 +16,8 @@ The **patch** part is incremented if multiple releases happen the same month
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
* autosysadmin-agent: upstream release 24.03.2
|
* 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: Add new variable to disable global customisation of bash config
|
||||||
* evolinux-base: Disable logcheck monitoring of journald only if journald.logfiles exists
|
* evolinux-base: Disable logcheck monitoring of journald only if journald.logfiles exists
|
||||||
* evolinux-users: Add sudo mvcli for nagios user
|
* evolinux-users: Add sudo mvcli for nagios user
|
||||||
|
|
|
@ -1,23 +1,16 @@
|
||||||
# evobackup-client
|
# 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
|
Additional information:
|
||||||
evobackup-client__hosts before running it.
|
|
||||||
|
|
||||||
The default zzz_evobackup.sh configures a system only backup, but the
|
* [evobackup-client documentation](https://gitea.evolix.org/evolix/evobackup/src/branch/master/client/README.md)
|
||||||
template can be overriden to configure a full backup instead. If
|
* canary
|
||||||
you change the variables in defaults/main.yml you can easily run
|
|
||||||
this again and configure backups to a second set of hosts.
|
|
||||||
|
|
||||||
Do not forget to set the evobackup-client__mail variable to an
|
## Available variables
|
||||||
email adress you control.
|
|
||||||
|
|
||||||
You can add this example to an installation playbook to create the
|
* `evobackup_client__lib_dir` : directory for libraries (default: `/usr/local/lib/evobackup`)
|
||||||
ssh key without running the rest of the role.
|
* `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`)
|
||||||
post_tasks:
|
* `evobackup_client__update_canary_who` : who the canary update must be attributed to (default: `@daily`)
|
||||||
- include_role:
|
|
||||||
name: evobackup-client tasks_from: ssh_key.yml
|
|
||||||
~~~
|
|
||||||
|
|
|
@ -1,15 +1,22 @@
|
||||||
---
|
---
|
||||||
evobackup_client__root_key_path: "/root/.ssh/id_ed25519"
|
# evobackup_client__root_key_path: "/root/.ssh/id_ed25519"
|
||||||
evobackup_client__root_key_type: "ed25519"
|
# evobackup_client__root_key_type: "ed25519"
|
||||||
evobackup_client__cron_path: "/etc/cron.daily/zzz_evobackup"
|
# evobackup_client__cron_path: "/etc/cron.daily/zzz_evobackup"
|
||||||
evobackup_client__cron_template_name: "zzz_evobackup"
|
# evobackup_client__cron_template_name: "zzz_evobackup"
|
||||||
evobackup_client__mail: null
|
# evobackup_client__mail: null
|
||||||
evobackup_client__servers_fallback: -1
|
# evobackup_client__servers_fallback: -1
|
||||||
evobackup_client__pid_path: "/var/run/evobackup.pid"
|
# evobackup_client__pid_path: "/var/run/evobackup.pid"
|
||||||
evobackup_client__log_path: "/var/log/evobackup.log"
|
# evobackup_client__log_path: "/var/log/evobackup.log"
|
||||||
evobackup_client__backup_path: "/home/backup"
|
# evobackup_client__backup_path: "/home/backup"
|
||||||
evobackup_client__hosts: null
|
# evobackup_client__hosts: null
|
||||||
# - name: "backups.example.org"
|
# - name: "backups.example.org"
|
||||||
# ip: "xxx.xxx.xxx.xxx"
|
# ip: "xxx.xxx.xxx.xxx"
|
||||||
# fingerprint: "ecdsa-sha2-nistp256 ..."
|
# fingerprint: "ecdsa-sha2-nistp256 ..."
|
||||||
# port: xxxx
|
# 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"
|
||||||
|
|
82
evobackup-client/files/upstream/CHANGELOG.md
Normal file
82
evobackup-client/files/upstream/CHANGELOG.md
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
# 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.04.1]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* evobackupctl: quote ARGS variable for options parsing.
|
||||||
|
|
||||||
|
## [24.04]
|
||||||
|
|
||||||
|
### 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]
|
||||||
|
|
||||||
|
### 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]
|
||||||
|
|
||||||
|
Split client and server parts of the project
|
153
evobackup-client/files/upstream/bin/evobackupctl
Normal file
153
evobackup-client/files/upstream/bin/evobackupctl
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# shellcheck disable=SC2155
|
||||||
|
readonly PROGNAME=$(basename "${0}")
|
||||||
|
# shellcheck disable=SC2155
|
||||||
|
readonly PROGDIR=$(readlink -m "$(dirname "${0}")")
|
||||||
|
# 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}"
|
||||||
|
|
||||||
|
sed -i "s|@COMMAND@|${PROGDIR}/${PROGNAME} ${ARGS}|" "${dest_path}"
|
||||||
|
sed -i "s|@DATE@|$(date --iso-8601=seconds)|" "${dest_path}"
|
||||||
|
sed -i "s|@VERSION@|${VERSION}|" "${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}
|
301
evobackup-client/files/upstream/lib/dump/elasticsearch.sh
Normal file
301
evobackup-client/files/upstream/lib/dump/elasticsearch.sh
Normal 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}"
|
||||||
|
}
|
559
evobackup-client/files/upstream/lib/dump/misc.sh
Normal file
559
evobackup-client/files/upstream/lib/dump/misc.sh
Normal 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}"
|
||||||
|
}
|
1551
evobackup-client/files/upstream/lib/dump/mysql.sh
Normal file
1551
evobackup-client/files/upstream/lib/dump/mysql.sh
Normal 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
|
||||||
|
}
|
343
evobackup-client/files/upstream/lib/dump/postgresql.sh
Normal file
343
evobackup-client/files/upstream/lib/dump/postgresql.sh
Normal 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}"
|
||||||
|
}
|
466
evobackup-client/files/upstream/lib/main.sh
Normal file
466
evobackup-client/files/upstream/lib/main.sh
Normal file
|
@ -0,0 +1,466 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# shellcheck disable=SC2034,SC2317
|
||||||
|
|
||||||
|
readonly VERSION="24.04.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}
|
||||||
|
}
|
143
evobackup-client/files/upstream/lib/utilities.sh
Normal file
143
evobackup-client/files/upstream/lib/utilities.sh
Normal 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'
|
||||||
|
}
|
326
evobackup-client/files/upstream/lib/zzz_evobackup.sh
Normal file
326
evobackup-client/files/upstream/lib/zzz_evobackup.sh
Normal 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
|
50
evobackup-client/tasks/install.yml
Normal file
50
evobackup-client/tasks/install.yml
Normal 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)
|
|
@ -1,26 +1,31 @@
|
||||||
---
|
---
|
||||||
|
|
||||||
- ansible.builtin.include: "ssh_key.yml"
|
- name: Install evobackup client components
|
||||||
tags:
|
ansible.builtin.include: "install.yml"
|
||||||
- evobackup_client
|
|
||||||
- evobackup_client_backup_ssh_key
|
|
||||||
|
|
||||||
- ansible.builtin.include: "jail.yml"
|
### This is commented because supposedly non-functionnal
|
||||||
tags:
|
|
||||||
- evobackup_client
|
|
||||||
- evobackup_client_jail
|
|
||||||
|
|
||||||
- ansible.builtin.include: "upload_scripts.yml"
|
# - ansible.builtin.include: "ssh_key.yml"
|
||||||
tags:
|
# tags:
|
||||||
- evobackup_client
|
# - evobackup_client
|
||||||
- evobackup_client_backup_scripts
|
# - evobackup_client_backup_ssh_key
|
||||||
|
|
||||||
- ansible.builtin.include: "open_ssh_ports.yml"
|
# - ansible.builtin.include: "jail.yml"
|
||||||
tags:
|
# tags:
|
||||||
- evobackup_client
|
# - evobackup_client
|
||||||
- evobackup_client_backup_firewall
|
# - evobackup_client_jail
|
||||||
|
|
||||||
- ansible.builtin.include: "verify_ssh.yml"
|
# - ansible.builtin.include: "upload_scripts.yml"
|
||||||
tags:
|
# tags:
|
||||||
- evobackup_client
|
# - evobackup_client
|
||||||
- evobackup_client_backup_hosts
|
# - 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
|
||||||
|
|
3
evobackup-client/templates/update-evobackup-canary.sh.j2
Normal file
3
evobackup-client/templates/update-evobackup-canary.sh.j2
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
update-evobackup-canary --who {{ evobackup_client__update_canary_who | mandatory }}
|
|
@ -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}
|
|
Loading…
Reference in a new issue