Compare commits

..

No commits in common. "master" and "split-lib" have entirely different histories.

22 changed files with 1990 additions and 3689 deletions

View file

@ -1,20 +0,0 @@
EvoBackup
=========
EvoBackup is a combination of tools to manage backups on Evolix servers.
## The client side
_What you install on the servers you want to backup._
There is a backup script (usually executed by cron or similar), a utility script and some libraries.
More information in the [client README](/evolix/evobackup/src/branch/master/client/README.md).
## The server side
_What you install on the servers that store the backups._
This is also known as `bkctld` : a program to manage SSH servers in chroots to isolate backup destinations, daily copies and data retention.
More information in the [server README](/evolix/evobackup/src/branch/master/server/README.md).

View file

@ -1,13 +1,8 @@
# 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
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
@ -15,6 +10,8 @@ The **patch** part changes is incremented if multiple releases happen the same m
### Changed
* split functions into libraries
### Deprecated
### Removed
@ -23,47 +20,7 @@ The **patch** part changes is incremented if multiple releases happen the same m
### Security
## [24.05] - 2022-05-02
### Added
* evobackupctl: update LIBDIR when copying the template
### Changed
* evobackupctl: simplify the program path retrieval
## [24.04.1] - 2022-04-30
### Fixed
* evobackupctl: quote ARGS variable for options parsing.
## [24.04] - 2022-04-29
### Added
* Vagrant definition for manual tests
### Changed
* split functions into libraries
* add evobackupctl script
* change the "zzz_evobackup" script to a template, easy to copy with evobackupctl
* use env-based shebang for shell scripts
* use $TMPDIR if available
### Removed
* update-evobackup-canary is managed by ansible-roles.git
* deployment by Ansible is managed elsewhere (now in evolix-private.git, later in ansible-roles.git)
### Fixed
* don't exit the whole program if a sync task can't be done
## [22.12] - 2022-12-27
## [22.12]
### Changed
* Use --dump-dir instead of --backup-dir to suppress dump-server-state warning
@ -87,6 +44,6 @@ The **patch** part changes is incremented if multiple releases happen the same m
* Make start_time and stop_time compatible with OpenBSD
## [22.03] - 2022-04-03
## [22.03]
Split client and server parts of the project

View file

@ -1,166 +0,0 @@
EvoBackup — the client side
===========================
_What you install on the servers you want to backup._
## Design
### backup script
The tip of the iceberg is a script (often called `zzz_evobackup` because it is executed by cron at the very end of the _daily_ tasks list).
This is where you setup **what**, **how** and **where** to backup on remote server(s).
There are 2 main phases in the backup :
1. **local tasks**: everything you want to do locally (save state information, dump databases…).
2. **sync tasks**: which data (including what has been prepared in the local phase) you want to send to remote servers.
### libraries
The vast majority of the logic is in libraries, to help maintaining them without having to modify the backup script.
They contain mostly _dump_ functions that are called from the backup script.
Those functions contain a lot of code for logging, options parsing, error management, and some code specific to the dump task.
### utility script
A scripts named `evobackupctl` helps initializing a backup jail on remote servers or install the backup script where you want.
## Install and update
To install, copy these files :
* `lib/*` → `/usr/local/lib/evobackup`
* `bin/evobackupctl` → `/usr/local/bin/evobackupctl`
To update, simply overwrite them, since their content should (must?) not be customized locally.
There is also an [evobackup-client](https://gitea.evolix.org/evolix/ansible-roles/src/branch/unstable/evobackup-client) Ansible role to do this (and some other stuff) automatically.
## Usage
### backup script
#### minimal configuration
##### mail notifications
The absolute minimum you must do is set the `MAIL` variable to the email address you want to be notified at.
##### sync tasks
If you want to sync files to a remote server, you have to set the `SERVERS` variable with at least one host and port.
Beware that the default `evolix-system` _sync_ doesn't sync `/home`, `/srv`…
If you want to sync files to multiple groups of servers, you can add as many _sync_ sections as you want.
A _sync_ section must contain something like this :
~~~bash
# The name of the "sync" (visible in logs)
SYNC_NAME="evolix-system"
# List of servers
SERVERS=(
host1:port1
host2:port2
)
# List of paths to include in the sync
RSYNC_INCLUDES=(
"${rsync_default_includes[@]}"
/etc
/root
/var
)
# List of paths to exclude from the sync
RSYNC_EXCLUDES=(
"${rsync_default_excludes[@]}"
)
# Actual sync command
sync "${SYNC_NAME}" "SERVERS[@]" "RSYNC_INCLUDES[@]" "RSYNC_EXCLUDES[@]"
~~~
##### local tasks
By default, the `local_tasks()` function only:
* executes [dump-server-state](https://gitea.evolix.org/evolix/dump-server-state) to put have a saved copy of a lot of information about the server
* saves a traceroute to some key network endpoints (using the `dump_traceroute()` function)
You can enable (by uncommenting) as many _dump_ functions as you want.
### advanced customization
Since this is a shell script, you can add any bash-compatible code you want.
If you do so you should read the libraries code to make sure that you don't overwrite existing functions.
##### sync tasks
If you don't want to sync files to any remote servers, you can simply replace the content of the `sync_tasks()` function by a no-op command (`:`).
`RSYNC_INCLUDES` and `RSYNC_EXCLUDES` refer to `${rsync_default_includes[@]}` and `${rsync_default_excludes[@]}` (defined in the `main.sh` library) to simplify the configuration. If you want to precisely customize the lists you can remove them and add you own.
##### local tasks
Existing _dump_ functions (as defined in libraries) are usable as-is, but you can also create your own local custom functions.
You have to define them in the backup script (or in a file that you source from the backup script).
You should prefix their name with `dump_` base your customization on the `dump_custom()` (documented in the backup script) to keep the boilerplate code (for logging, error management…).
You can customize some values inside the `setup_custom()`, like the server's hostname, the notification mail subject…
If you want to source libraries from a different path, you can change the `LIBDIR` variable at the end of the backup script.
### utility tool
The command is `evobackupctl`.
~~~
# evobackupctl --help
evobackupctl 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
# evobackupctl --version
evobackupctl version 24.04
Copyright 2024 Evolix <info@evolix.fr>,
Jérémy Lecour <jlecour@evolix.fr>.
evobackupctl 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.
~~~
#### jail init commands
It prints a list of commands you can execute on remote backup servers to configure a backup "jail".
~~~
# evobackupctl --jail-init-commands
Copy-paste those lines on backup server(s) :
----------
SERVER_NAME=example-hostname
SERVER_IP=203.0.113.1
echo 'ssh-ed25519 xxxxxx root@example-hostname' > /root/${SERVER_NAME}.pub
bkctld init ${SERVER_NAME}
bkctld key ${SERVER_NAME} /root/${SERVER_NAME}.pub
bkctld ip ${SERVER_NAME} ${SERVER_IP}
bkctld start ${SERVER_NAME}
bkctld status ${SERVER_NAME}
grep --quiet --extended-regexp "^\s?NODE=" /etc/default/bkctld && bkctld sync ${SERVER_NAME}
----------
~~~
#### copy-template
It copies the backups script template to the path of your choice, nothing more.
~~~
# evobackupctl --copy-template /etc/cron.daily/zzz_evobackup
New evobackup script has been saved to '/etc/cron.daily/zzz_evobackup'.
Remember to customize it (mail notifications, backup servers…).
~~~

49
client/Vagrantfile vendored
View file

@ -1,49 +0,0 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
# Load ~/.VagrantFile if exist, permit local config provider
vagrantfile = File.join(Dir.home, ".VagrantFile")
load File.expand_path(vagrantfile) if File.exist?(vagrantfile)
Vagrant.configure("2") do |config|
# Run "vagrant rsync-auto" to sync after each change
config.vm.synced_folder ".", "/vagrant", type: "rsync", disabled: true
config.vm.synced_folder "bin", "/usr/local/bin", type: "rsync"
config.vm.synced_folder "lib", "/usr/local/lib/evobackup", type: "rsync"
config.ssh.shell = "/bin/sh"
config.vm.provider :libvirt do |libvirt|
# libvirt.storage :file, :size => '10G', :device => 'vdb'
libvirt.memory = 1024
libvirt.cpus = 1
end
config_script = <<~SCRIPT
set -e
sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/; s/# fr_FR.UTF-8 UTF-8/fr_FR.UTF-8 UTF-8/' /etc/locale.gen && \
echo 'LANG="fr_FR.UTF-8"'>/etc/default/locale && \
dpkg-reconfigure --frontend=noninteractive locales && \
update-locale LANG=fr_FR.UTF-8
exit 0
SCRIPT
[
{version: "buster"},
{version: "bullseye"},
{version: "bookworm"}
].each do |i|
config.vm.define(i[:version].to_s) do |node|
node.vm.hostname = "evobackup-#{i[:version]}"
node.vm.box = "debian/#{i[:version]}64"
node.vm.provision "config", type: "shell", inline: config_script
node.vm.provision :ansible do |ansible|
ansible.playbook = "vagrant.yml"
end
end
end
end

View file

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

View file

@ -0,0 +1,49 @@
---
- hosts: all
gather_facts: yes
become: yes
vars:
evobackup_script_path: /etc/cron.daily/zzz_evobackup_beta
evobackup_mail: alert4@evolix.net
evobackup_libdir: "/usr/local/lib/evobackup"
tasks:
- name: LIBDIR is present
file:
path: "{{ evobackup_libdir }}"
state: directory
- name: libraries are installed
copy:
src: "{{ item }}"
dest: "{{ evobackup_libdir }}/"
remote_src: False
owner: root
group: root
mode: "0640"
force: yes
loop: "{{ lookup('fileglob', 'lib/*.sh', wantlist=True) }}"
- name: script is present
copy:
src: zzz_evobackup.sh
dest: "{{ evobackup_script_path }}"
remote_src: False
owner: root
group: root
mode: "0750"
force: no
- name: Email is customized
replace:
dest: /etc/cron.daily/zzz_evobackup
regexp: "^MAIL=.*"
replace: "MAIL={{ evobackup_mail }}"
- name: LIBDIR is customized
replace:
dest: /etc/cron.daily/zzz_evobackup
regexp: "^LIBDIR=.*"
replace: "LIBDIR=\"{{ evobackup_libdir }}\""

1476
client/lib/dump.sh Normal file
View file

@ -0,0 +1,1476 @@
#!/bin/bash
# shellcheck disable=SC2034,SC2317
mysql_list_databases() {
port=${1:-"3306"}
mysql --defaults-extra-file=/etc/mysql/debian.cnf --port="${port}" --execute="show databases" --silent --skip-column-names \
| grep --extended-regexp --invert-match "^(Database|information_schema|performance_schema|sys)"
}
### BEGIN Dump functions ####
#######################################################################
# 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}"
# shellcheck disable=SC2174
mkdir -p -m 700 "${dump_dir}"
log "LOCAL_TASKS - start dump_ldap to ${dump_dir}"
slapcat -n 0 -l "${dump_dir}/config.bak"
slapcat -n 1 -l "${dump_dir}/data.bak"
slapcat -l "${dump_dir}/all.bak"
log "LOCAL_TASKS - stop dump_ldap"
}
#######################################################################
# Dump a single compressed file of all databases of an instance
#
# Arguments:
# --masterdata (default: <absent>)
# --port=[Integer] (default: 3306)
#######################################################################
dump_mysql_global() {
local dump_dir="${LOCAL_BACKUP_DIR}/mysql-global"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
# shellcheck disable=SC2174
mkdir -p -m 700 "${dump_dir}" "${errors_dir}"
local error_file="${errors_dir}/mysql.bak.err"
local dump_file="${dump_dir}/mysql.bak.gz"
log "LOCAL_TASKS - start ${dump_file}"
local option_masterdata=""
local option_port="3306"
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
--masterdata)
option_masterdata="--masterdata"
;;
--port)
# port options, with value separated by space
if [ -n "$2" ]; then
option_port="${2}"
shift
else
log_error "LOCAL_TASKS - '--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 - '--port' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - unkwnown option (ignored): '${1}'"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
declare -a options
options=()
options+=(--defaults-extra-file=/etc/mysql/debian.cnf)
options+=(--port="${option_port}")
options+=(--opt)
options+=(--force)
options+=(--events)
options+=(--hex-blob)
options+=(--all-databases)
if [ -n "${option_masterdata}" ]; then
options+=("${option_masterdata}")
fi
mysqldump "${options[@]}" 2> "${error_file}" | gzip --best > "${dump_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - mysqldump to ${dump_file} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - stop ${dump_file}"
}
#######################################################################
# Dump a compressed file per database of an instance
#
# Arguments:
# --port=[Integer] (default: 3306)
#######################################################################
dump_mysql_per_base() {
local dump_dir="${LOCAL_BACKUP_DIR}/mysql-per-base"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
# shellcheck disable=SC2174
mkdir -p -m 700 "${dump_dir}" "${errors_dir}"
local option_port="3306"
# 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 - '--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 - '--port' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - unkwnown option (ignored): '${1}'"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
declare -a options
options=()
options+=(--defaults-extra-file=/etc/mysql/debian.cnf)
options+=(--port="${option_port}")
options+=(--force)
options+=(--events)
options+=(--hex-blob)
databases=$(mysql_list_databases ${option_port})
for database in ${databases}; do
local error_file="${errors_dir}/${database}.err"
local dump_file="${dump_dir}/${database}.sql.gz"
log "LOCAL_TASKS - start ${dump_file}"
mysqldump "${options[@]}" "${database}" 2> "${error_file}" | gzip --best > "${dump_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - mysqldump to ${dump_file} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - stop ${dump_file}"
done
}
#######################################################################
# Dump grants, variables and databases schemas for an instance
#
# Arguments:
# --port=[Integer] (default: 3306)
#######################################################################
dump_mysql_meta() {
local dump_dir="${LOCAL_BACKUP_DIR}/mysql-meta"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
# shellcheck disable=SC2174
mkdir -p -m 700 "${dump_dir}" "${errors_dir}"
local option_port="3306"
# 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 - '--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 - '--port' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - unkwnown option (ignored): '${1}'"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
## Dump all grants (requires 'percona-toolkit' package)
local error_file="${errors_dir}/all_grants.err"
local dump_file="${dump_dir}/all_grants.sql"
log "LOCAL_TASKS - start ${dump_file}"
declare -a options
options=()
options+=(--port "${option_port}")
options+=(--flush)
options+=(--no-header)
pt-show-grants "${options[@]}" 2> "${error_file}" > "${dump_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - 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 - stop ${dump_file}"
## Dump all variables
local error_file="${errors_dir}/variables.err"
local dump_file="${dump_dir}/variables.txt"
log "LOCAL_TASKS - start ${dump_file}"
declare -a options
options=()
options+=(--port="${option_port}")
options+=(--no-auto-rehash)
options+=(-e "SHOW GLOBAL VARIABLES;")
mysql "${options[@]}" 2> "${error_file}" > "${dump_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - mysql 'show variables' returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - stop ${dump_file}"
## Schema only (no data) for each databases
databases=$(mysql_list_databases "${option_port}")
for database in ${databases}; do
local error_file="${errors_dir}/${database}.schema.err"
local dump_file="${dump_dir}/${database}.schema.sql"
log "LOCAL_TASKS - start ${dump_file}"
declare -a options
options=()
options+=(--defaults-extra-file=/etc/mysql/debian.cnf)
options+=(--port="${option_port}")
options+=(--force)
options+=(--no-data)
options+=(--databases "${database}")
mysqldump "${options[@]}" 2> "${error_file}" > "${dump_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - mysqldump to ${dump_file} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - stop ${dump_file}"
done
}
#######################################################################
# Dump "tabs style" separate schema/data for each database of an instance
#
# Arguments:
# --port=[Integer] (default: 3306)
#######################################################################
dump_mysql_tabs() {
databases=$(mysql_list_databases 3306)
for database in ${databases}; do
local dump_dir="${LOCAL_BACKUP_DIR}/mysql-tabs/${database}"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
# shellcheck disable=SC2174
mkdir -p -m 700 "${dump_dir}" "${errors_dir}"
chown -RL mysql "${dump_dir}"
local error_file="${errors_dir}.err"
log "LOCAL_TASKS - start ${dump_dir}"
local option_port="3306"
# 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 - '--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 - '--port' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - unkwnown option (ignored): '${1}'"
printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
declare -a options
options=()
options+=(--defaults-extra-file=/etc/mysql/debian.cnf)
options+=(--port="${option_port}")
options+=(--force)
options+=(--quote-names)
options+=(--opt)
options+=(--events)
options+=(--hex-blob)
options+=(--skip-comments)
options+=(--fields-enclosed-by='\"')
options+=(--fields-terminated-by=',')
options+=(--tab="${dump_dir}")
options+=("${database}")
mysqldump "${options[@]}" 2> "${error_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - mysqldump to ${dump_dir} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - stop ${dump_dir}"
done
}
#######################################################################
# Dump a single file for all databases of an instance
# using a custom authentication, instead of /etc/mysql/debian.cnf
#
# Arguments:
# --port=[Integer] (default: 3306)
# --user=[String] (default: <blank>)
# --password=[String] (default: <blank>)
#######################################################################
dump_mysql_instance() {
local dump_dir="${LOCAL_BACKUP_DIR}/mysql-instances"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
# shellcheck disable=SC2174
mkdir -p -m 700 "${dump_dir}" "${errors_dir}"
local option_port=""
local option_user=""
local option_password=""
# 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 - '--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 - '--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 - '--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 - '--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 - '--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 - '--password' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - unkwnown option (ignored): '${1}'"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
declare -a options
options=()
options+=(--port="${option_port}")
options+=(--user="${option_user}")
options+=(--password="${option_password}")
options+=(--force)
options+=(--opt)
options+=(--all-databases)
options+=(--events)
options+=(--hex-blob)
local error_file="${errors_dir}/${option_port}.err"
local dump_file="${dump_dir}/${option_port}.bak.gz"
log "LOCAL_TASKS - start ${dump_file}"
mysqldump "${options[@]}" 2> "${error_file}" | gzip --best > "${dump_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - mysqldump to ${dump_file} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - stop ${dump_file}"
}
#######################################################################
# Dump a single file of all PostgreSQL databases
#
# Arguments: <none>
#######################################################################
dump_postgresql_global() {
local dump_dir="${LOCAL_BACKUP_DIR}/postgresql-global"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
# shellcheck disable=SC2174
mkdir -p -m 700 "${dump_dir}" "${errors_dir}"
## example with pg_dumpall and with compression
local dump_file="${dump_dir}/pg.dump.bak.gz"
log "LOCAL_TASKS - start ${dump_file}"
(sudo -u postgres pg_dumpall) 2> "${error_file}" | gzip --best > "${dump_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - 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 - stop ${dump_file}"
## example with pg_dumpall and without compression
## WARNING: you need space in ~postgres
# local dump_file="${dump_dir}/pg.dump.bak"
# log "LOCAL_TASKS - start ${dump_file}"
#
# (su - postgres -c "pg_dumpall > ~/pg.dump.bak") 2> "${error_file}"
# mv ~postgres/pg.dump.bak "${dump_file}"
#
# log "LOCAL_TASKS - stop ${dump_file}"
}
#######################################################################
# Dump a compressed file per database
#
# Arguments: <none>
#######################################################################
dump_postgresql_per_base() {
local dump_dir="${LOCAL_BACKUP_DIR}/postgresql-per-base"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
# shellcheck disable=SC2174
mkdir -p -m 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.gz"
log "LOCAL_TASKS - start ${dump_file}"
(sudo -u postgres /usr/bin/pg_dump --create -s -U postgres -d "${database}") 2> "${error_file}" | gzip --best > "${dump_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - 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 - 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}"
# shellcheck disable=SC2174
mkdir -p -m 700 "${dump_dir}" "${errors_dir}"
local error_file="${errors_dir}/pg-backup.err"
local dump_file="${dump_dir}/pg-backup.tar"
log "LOCAL_TASKS - 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 - 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 - stop ${dump_file}"
}
#######################################################################
# Copy dump file of Redis instances
#
# Arguments:
# --instances=[Integer] (default: all)
#######################################################################
dump_redis() {
all_instances=$(find /var/lib/ -mindepth 1 -maxdepth 1 -type d -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 - '--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 - '--instances' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - unkwnown option (ignored): '${1}'"
;;
*)
# 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}"
# shellcheck disable=SC2174
mkdir -p -m 700 "${dump_dir}" "${errors_dir}"
if [ -f "${instance}/dump.rdb" ]; then
local error_file="${errors_dir}/${instance}.err"
log "LOCAL_TASKS - start ${dump_dir}"
cp -a "${instance}/dump.rdb" "${dump_dir}/" 2> "${error_file}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - cp ${instance}/dump.rdb to ${dump_dir} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - stop ${dump_dir}"
else
log_error "LOCAL_TASKS - '${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:
# --user=[String] (default: <blank>)
# --password=[String] (default: <blank>)
#######################################################################
dump_mongodb() {
## don't forget to create use with read-only access
## > use admin
## > db.createUser( { user: "mongobackup", pwd: "PASS", roles: [ "backup", ] } )
local dump_dir="${LOCAL_BACKUP_DIR}/mongodump"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
# shellcheck disable=SC2174
mkdir -p -m 700 "${dump_dir}" "${errors_dir}"
local error_file="${errors_dir}.err"
log "LOCAL_TASKS - start ${dump_dir}"
local option_user=""
local option_password=""
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case ${1:-''} in
--user)
# user options, with value separated by space
if [ -n "$2" ]; then
option_user="${2}"
shift
else
log_error "LOCAL_TASKS - '--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 - '--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 - '--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 - '--password' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - unkwnown option (ignored): '${1}'"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
declare -a options
options=()
options+=(--username="${option_user}")
options+=(--password="${option_password}")
options+=(--out="${dump_dir}/")
mongodump "${options[@]}" 2> "${error_file}" > /dev/null
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - 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 ${dump_dir}"
}
#######################################################################
# Dump MegaCLI configuration
#
# Arguments: <none>
#######################################################################
dump_megacli_config() {
local dump_dir="${LOCAL_BACKUP_DIR}/megacli"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
# shellcheck disable=SC2174
mkdir -p -m 700 "${dump_dir}" "${errors_dir}"
local dump_file="${dump_dir}/megacli.cfg"
local error_file="${errors_dir}/megacli.err"
log "LOCAL_TASKS - start ${dump_file}"
megacli -CfgSave -f "${dump_file}" -a0 2> "${error_file}" > /dev/null
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - megacli to ${dump_file} returned an error ${last_rc}" "${error_file}"
GLOBAL_RC=${E_DUMPFAILED}
else
rm -f "${error_file}"
fi
log "LOCAL_TASKS - stop ${dump_file}"
}
#######################################################################
# Save some traceroute/mtr results
#
# Arguments:
# --targets=[IP,HOST] (default: <none>)
#######################################################################
dump_traceroute() {
local dump_dir="${LOCAL_BACKUP_DIR}/traceroute"
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
rm -rf "${dump_dir}" "${errors_dir}"
# shellcheck disable=SC2174
mkdir -p -m 700 "${dump_dir}" "${errors_dir}"
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 - '--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 - '--targets' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - unkwnown option (ignored): '${1}'"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
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 - start ${dump_file}"
${mtr_bin} -r "${target}" > "${dump_file}"
log "LOCAL_TASKS - 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 - start ${dump_file}"
${traceroute_bin} -n "${target}" > "${dump_file}" 2>&1
log "LOCAL_TASKS - 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
# shellcheck disable=SC2174
# mkdir -p -m 700 "${dump_dir}"
log "LOCAL_TASKS - 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 - dump-server-state is missing"
rc=1
else
${dump_server_state_bin} "${options[@]}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - dump-server-state returned an error ${last_rc}, check ${dump_dir}"
GLOBAL_RC=${E_DUMPFAILED}
fi
fi
log "LOCAL_TASKS - 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}"
# shellcheck disable=SC2174
mkdir -p -m 700 "${dump_dir}" "${errors_dir}"
local error_file="${errors_dir}.err"
local dump_file="${dump_dir}/config"
log "LOCAL_TASKS - start ${dump_file}"
rabbitmqadmin export "${dump_file}" 2> "${error_file}" >> "${LOGFILE}"
local last_rc=$?
# shellcheck disable=SC2086
if [ ${last_rc} -ne 0 ]; then
log_error "LOCAL_TASKS - 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 - 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}"
# shellcheck disable=SC2174
mkdir -p -m 700 "${dump_dir}" "${errors_dir}"
log "LOCAL_TASKS - start ${dump_dir}"
getfacl -R /etc > "${dump_dir}/etc.txt"
getfacl -R /home > "${dump_dir}/home.txt"
getfacl -R /usr > "${dump_dir}/usr.txt"
getfacl -R /var > "${dump_dir}/var.txt"
log "LOCAL_TASKS - stop ${dump_dir}"
}
#######################################################################
# Snapshot Elasticsearch data (single-node cluster)
#
# Arguments:
# --protocol=[String] (default: http)
# --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_snapshot_singlenode() {
log "LOCAL_TASKS - start dump_elasticsearch_snapshot_singlenode"
local option_protocol="http"
local option_host="localhost"
local option_port="9200"
local option_user=""
local option_password=""
local option_repository="snaprepo"
local option_snapshot="snapshot.daily"
# 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 - '--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 - '--protocol' 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 - '--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 - '--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 - '--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 - '--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 - '--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 - '--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 - '--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 - '--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 - '--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 - '--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 - '--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 - '--snapshot' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - unkwnown option (ignored): '${1}'"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
## 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 snapshot_url="${base_url}/_snapshot/${option_repository}/${option_snapshot}"
if [ -n "${option_user}" ] || [ -n "${option_password}" ]; then
local option_auth="--user ${option_user}:${option_password}"
else
local option_auth=""
fi
curl -s -XDELETE "${option_auth}" "${snapshot_url}" >> "${LOGFILE}"
curl -s -XPUT "${option_auth}" "${snapshot_url}?wait_for_completion=true" >> "${LOGFILE}"
# Clustered version here
# It basically the same thing except that you need to check that NFS is mounted
# if ss | grep ':nfs' | grep -q 'ip\.add\.res\.s1' && ss | grep ':nfs' | grep -q 'ip\.add\.res\.s2'
# then
# curl -s -XDELETE "${option_auth}" "${snapshot_url}" >> "${LOGFILE}"
# curl -s -XPUT "${option_auth}" "${snapshot_url}?wait_for_completion=true" >> "${LOGFILE}"
# else
# echo 'Cannot make a snapshot of elasticsearch, at least one node is not mounting the repository.'
# fi
log "LOCAL_TASKS - stop dump_elasticsearch_snapshot_singlenode"
}
#######################################################################
# Snapshot Elasticsearch data (multi-node cluster)
#
# Arguments:
# --protocol=[String] (default: http)
# --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)
# --nfs-server=[IP|HOST] (default: <none>)
#######################################################################
dump_elasticsearch_snapshot_multinode() {
log "LOCAL_TASKS - start dump_elasticsearch_snapshot_multinode"
local option_protocol="http"
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_nfs_server=""
# 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 - '--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 - '--protocol' 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 - '--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 - '--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 - '--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 - '--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 - '--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 - '--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 - '--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 - '--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 - '--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 - '--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 - '--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 - '--snapshot' requires a non-empty option argument."
exit 1
;;
--nfs-server)
# nfs-server options, with value separated by space
if [ -n "$2" ]; then
option_nfs_server="${2}"
shift
else
log_error "LOCAL_TASKS - '--nfs-server' requires a non-empty option argument."
exit 1
fi
;;
--nfs-server=?*)
# nfs-server options, with value separated by =
option_nfs_server="${1#*=}"
;;
--nfs-server=)
# nfs-server options, without value
log_error "LOCAL_TASKS - '--nfs-server' requires a non-empty option argument."
exit 1
;;
--)
# End of all options.
shift
break
;;
-?*|[[:alnum:]]*)
# ignore unknown options
log_error "LOCAL_TASKS - unkwnown option (ignored): '${1}'"
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
## 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 snapshot_url="${base_url}/_snapshot/${option_repository}/${option_snapshot}"
if [ -n "${option_user}" ] || [ -n "${option_password}" ]; then
local option_auth="--user ${option_user}:${option_password}"
else
local option_auth=""
fi
# Clustered version here
# It basically the same thing except that you need to check that NFS is mounted
if ss | grep ':nfs' | grep -q -F "${option_nfs_server}"; then
curl -s -XDELETE "${option_auth}" "${snapshot_url}" >> "${LOGFILE}"
curl -s -XPUT "${option_auth}" "${snapshot_url}?wait_for_completion=true" >> "${LOGFILE}"
else
echo 'Cannot make a snapshot of elasticsearch, at least one node is not mounting the repository.'
fi
log "LOCAL_TASKS - stop dump_elasticsearch_snapshot_multinode"
}

View file

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

View file

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

View file

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

View file

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

View file

@ -1,27 +1,20 @@
#!/usr/bin/env bash
#!/bin/bash
# shellcheck disable=SC2034,SC2317
readonly VERSION="24.05"
readonly VERSION="23.1-pre"
# 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
set -u
# 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"
source "${LIBDIR}/dump.sh"
# Called from main, it is wrapping the local_tasks function defined in the real script
local_tasks_wrapper() {
@ -51,8 +44,6 @@ sync_tasks_wrapper() {
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
@ -63,8 +54,6 @@ sync_tasks_wrapper() {
)
;;
*bsd)
# NOTE: remember to single-quote paths if they contain globs (*)
# and you want to defer expansion
declare -a rsync_default_includes=(
/bin
/bsd
@ -82,8 +71,6 @@ sync_tasks_wrapper() {
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
@ -96,15 +83,14 @@ sync_tasks_wrapper() {
/usr/src
/var/apt
/var/cache
'/var/db/munin/*.tmp'
/var/db/munin/*.tmp
/var/lib/amavis/amavisd.sock
/var/lib/amavis/tmp
/var/lib/amavis/virusmails
'/var/lib/clamav/*.tmp'
/var/lib/clamav/*.tmp
/var/lib/elasticsearch
/var/lib/metche
/var/lib/mongodb
'/var/lib/munin/*tmp*'
/var/lib/munin/*tmp*
/var/lib/mysql
/var/lib/php/sessions
/var/lib/php5
@ -119,20 +105,20 @@ sync_tasks_wrapper() {
/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'
.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
@ -154,8 +140,6 @@ sync() {
## 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
@ -175,13 +159,8 @@ sync() {
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
server=$(pick_server ${n})
test $? = 0 || exit ${E_NOSRVAVAIL}
if test_server "${server}"; then
break
@ -192,13 +171,13 @@ sync() {
done
else
# we force the server
server=$(pick_server "${n}" "${sync_name}")
server=$(pick_server "${n}")
fi
rsync_server=$(echo "${server}" | cut -d':' -f1)
rsync_port=$(echo "${server}" | cut -d':' -f2)
log "SYNC_TASKS - sync=${sync_name}: use ${server}"
log "START SYNC_TASKS - \"${sync_name}\" : server=${server}"
# Rsync complete log file for the current run
RSYNC_LOGFILE="/var/log/${PROGNAME}.${sync_name}.rsync.log"
@ -222,7 +201,7 @@ sync() {
if [ -n "${mtree_bin}" ]; then
# Dump filesystem stats with mtree
log "SYNC_TASKS - sync=${sync_name}: start mtree"
log "SYNC_TASKS - start mtree"
# Loop over Rsync includes
for i in "${!rsync_includes[@]}"; do
@ -246,15 +225,15 @@ sync() {
done
if [ "${#mtree_files[@]}" -le 0 ]; then
log_error "SYNC_TASKS - ${sync_name}: ERROR: mtree didn't produce any file"
log_error "SYNC_TASKS - ERROR: mtree didn't produce any file"
fi
log "SYNC_TASKS - sync=${sync_name}: stop mtree (files: ${mtree_files[*]})"
log "SYNC_TASKS - stop mtree (files: ${mtree_files[*]})"
else
log "SYNC_TASKS - sync=${sync_name}: skip mtree (missing)"
log "SYNC_TASKS - skip mtree (missing)"
fi
else
log "SYNC_TASKS - sync=${sync_name}: skip mtree (disabled)"
log "SYNC_TASKS - skip mtree (disabled)"
fi
rsync_bin=$(command -v rsync)
@ -288,7 +267,7 @@ sync() {
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[*]}"
log "SYNC_TASKS - \"${sync_name}\" Rsync main command : ${rsync_bin} ${rsync_main_args[*]}"
# … execute it
${rsync_bin} "${rsync_main_args[@]}"
@ -302,7 +281,7 @@ sync() {
# 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}"
log_error "SYNC_TASKS - ${sync_name} Rsync main command returned an error ${rsync_main_rc}" "${LOGFILE}"
GLOBAL_RC=${E_SYNCFAILED}
else
# Build the report Rsync command
@ -331,13 +310,13 @@ sync() {
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[*]}"
log "SYNC_TASKS - ${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}"
log "STOP SYNC_TASKS - ${sync_name} server=${server}"
}
setup() {
@ -415,11 +394,11 @@ setup() {
# 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
trap "clean_temp_files" EXIT
}
run_evobackup() {
main() {
# Start timer
START_EPOCH=$(/bin/date +%s)
START_TIME=$(/bin/date +"%Y%m%d%H%M%S")
@ -463,4 +442,4 @@ run_evobackup() {
send_mail
exit ${GLOBAL_RC}
}
}

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/bash
# Output a message to the log file
log() {
@ -37,10 +37,9 @@ add_to_temp_files() {
TEMP_FILES+=("${1}")
}
# Remove all temporary file created during the execution
cleanup() {
clean_temp_files() {
# shellcheck disable=SC2086
rm -f "${TEMP_FILES[@]}"
find "${ERRORS_DIR}" -type d -empty -delete
}
enforce_single_process() {
local pidfile=$1
@ -105,12 +104,10 @@ test_server() {
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}")
@ -136,8 +133,4 @@ pick_server() {
send_mail() {
tail -20 "${LOGFILE}" | mail -s "${MAIL_SUBJECT}" "${MAIL}"
}
path_to_str() {
echo "${1}" | sed -e 's|^/||; s|/$||; s|/|:|g'
}
}

View file

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

View file

@ -0,0 +1,129 @@
#!/bin/sh
PROGNAME="update-evobackup-canary"
REPOSITORY="https://gitea.evolix.org/evolix/evobackup"
VERSION="22.06"
readonly VERSION
# base functions
show_version() {
cat <<END
${PROGNAME} version ${VERSION}
Copyright 2022 Evolix <info@evolix.fr>,
Jérémy Lecour <jlecour@evolix.fr>,
and others.
${REPOSITORY}
${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} is updating a canary file for evobackup.
Usage: ${PROGNAME} [OPTIONS]
Main options
-w, --who who has updated the file (default: logname())
-f, --file path of the canary file (default: /zzz_evobackup_canary)
-V, --version print version and exit
-h, --help print this message and exit
END
}
main() {
if [ -z "${who:-}" ]; then
who=$(logname)
fi
if [ -z "${canary_file:-}" ]; then
canary_file="/zzz_evobackup_canary"
fi
# This option is supported both on OpenBSD which does not use GNU date and on Debian
date=$(date "+%FT%T%z")
printf "%s %s\n" "${date}" "${who}" >> "${canary_file}"
}
# parse options
# based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
case $1 in
-h|-\?|--help)
show_help
exit 0
;;
-V|--version)
show_version
exit 0
;;
-w|--who)
# with value separated by space
if [ -n "$2" ]; then
who=$2
shift
else
printf 'ERROR: "-w|--who" requires a non-empty option argument.\n' >&2
exit 1
fi
;;
--who=?*)
# with value speparated by =
who=${1#*=}
;;
--who=)
# without value
printf 'ERROR: "--who" requires a non-empty option argument.\n' >&2
exit 1
;;
-f|--file)
# with value separated by space
if [ -n "$2" ]; then
canary_file=$2
shift
else
printf 'ERROR: "-f|--file" requires a non-empty option argument.\n' >&2
exit 1
fi
;;
--file=?*)
# with value speparated by =
canary_file=${1#*=}
;;
--file=)
# without value
printf 'ERROR: "--file" requires a non-empty option argument.\n' >&2
exit 1
;;
--)
# End of all options.
shift
break
;;
-?*)
# ignore unknown options
printf 'WARN: Unknown option : %s\n' "$1" >&2
exit 1
;;
*)
# Default case: If no more options then break out of the loop.
break
;;
esac
shift
done
export LC_ALL=C
set -u
main

View file

@ -1,76 +0,0 @@
# To be used through "vagrant up" or "vagrant provision".
---
- hosts: bookworm,bullseye,buster
gather_facts: yes
become: yes
vars_files:
- '~/GIT/evolix-private/vars/evolinux-secrets.yml'
vars:
evolinux_hostname: "localhost"
evolinux_domain: "localdomain.tld"
evomaintenance_alert_email: "evomaintenance-{{ evolinux_hostname }}@evolix.fr"
evomaintenance_install_vendor: True
client_number: "XXX"
monitoring_mode: "everytime"
evocheck_force_install: "local"
evoadmin_host: "evoadmin.{{ evolinux_hostname }}.evolix.eu"
evoadmin_contact_email: root@localhost
postfix_slow_transport_include: True
evolinux_ssh_allow_current_user: True
minifirewall_additional_trusted_ips: ["192.168.0.0/16", "10.0.0.0/8"]
minifirewall_http_sites: ["0.0.0.0/0"]
packweb_enable_evoadmin_vhost: True
packweb_phpmyadmin_suffix: "uE34swx9"
evolinux_apt_include: True
evolinux_etcgit_include: True
evolinux_hostname_include: True
evolinux_kernel_include: True
evolinux_fstab_include: True
evolinux_packages_include: True
evolinux_system_include: True
evolinux_evomaintenance_include: True
evolinux_ssh_include: True
evolinux_users_include: False
evolinux_root_include: True
evolinux_postfix_include: True
evolinux_logs_include: True
evolinux_default_www_include: True
evolinux_hardware_include: True
evolinux_provider_online_include: False
evolinux_provider_orange_fce_include: False
evolinux_log2mail_include: True
evolinux_minifirewall_include: True
evolinux_munin_include: True
evolinux_nagios_nrpe_include: True
evolinux_fail2ban_include: False
mysql_custom_datadir: '/home/mysql'
mysql_custom_tmpdir: '/home/tmpmysql'
mysql_custom_logdir: '/home/mysql-logs'
# evolinux_apt_public_sources: False
apt_upgrade: True
# TODO Try to to make it work without the following line
# packweb_multiphp_versions:
# - php74
# - php82
# autosysadmin_config:
# repair_http: "on"
# repair_mysql: off
# repair_all: 'off'
roles:
- mysql
# - evolinux-base
# # - evolinux-users
# - ./ansible/roles/autosysadmin-agent
# - packweb-apache
# # - redis
# - { role: redis, redis_instance_name: foo, redis_port: 6380 }
# - { role: redis, redis_instance_name: bar, redis_port: 6381 }

272
client/zzz_evobackup Normal file
View file

@ -0,0 +1,272 @@
#!/bin/bash
#
# Script Evobackup client
# See https://gitea.evolix.org/evolix/evobackup
#
# Authors: Evolix <info@evolix.fr>,
# Gregory Colpart <reg@evolix.fr>,
# Romain Dessort <rdessort@evolix.fr>,
# Benoit SĂ©rie <bserie@evolix.fr>,
# Tristan Pilat <tpilat@evolix.fr>,
# Victor Laborie <vlaborie@evolix.fr>,
# Jérémy Lecour <jlecour@evolix.fr>
# and others.
#
# Licence: AGPLv3
#######################################################################
#
# You must configure the MAIL variable to receive notifications.
#
# There is some optional configuration that you can do
# at the end of this script.
#
# The library (usually installed at /usr/local/lib/evobackup/main.sh)
# also has many variables that you can override for fine-tuning.
#
#######################################################################
# Email adress for notifications
MAIL=jdoe@example.com
#######################################################################
#
# The "sync_tasks" function will be called by the main 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)
#
# The "sync" function can be called multiple times
# with a different set of variables.
# That way you can to sync to various destinations.
#
#######################################################################
sync_tasks() {
########## System-only backup (to Evolix servers) #################
# Name your sync task, for logs
sync_name="evolix-system"
# List of host/port for your sync task
# shellcheck disable=SC2034
SERVERS=(
node0.backup.evolix.net:2234
node1.backup.evolix.net:2234
)
# What to include in your sync task
# Add or remove paths if you need
# shellcheck disable=SC2034
RSYNC_INCLUDES=(
"${rsync_default_includes[@]}"
/etc
/root
/var
)
# What to exclude from your sync task
# Add or remove paths if you need
# shellcheck disable=SC2034
RSYNC_EXCLUDES=(
"${rsync_default_excludes[@]}"
)
# Call the sync task
sync "${sync_name}" "SERVERS[@]" "RSYNC_INCLUDES[@]" "RSYNC_EXCLUDES[@]"
########## Full backup (to client servers) ########################
# Name your sync task, for logs
sync_name="client-full"
# List of host/port for your sync task
# shellcheck disable=SC2034
SERVERS=(
client-backup00.evolix.net:2221
client-backup01.evolix.net:2221
)
# What to include in your sync task
# Add or remove paths if you need
# shellcheck disable=SC2034
RSYNC_INCLUDES=(
"${rsync_default_includes[@]}"
/etc
/root
/var
/home
/srv
)
# What to exclude from your sync task
# Add or remove paths if you need
# shellcheck disable=SC2034
RSYNC_EXCLUDES=(
"${rsync_default_excludes[@]}"
)
# Call the sync task
sync "${sync_name}" "SERVERS[@]" "RSYNC_INCLUDES[@]" "RSYNC_EXCLUDES[@]"
}
#######################################################################
#
# The "local_tasks" function will be called by the main 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() {
########## OpenLDAP ###############
### dump_ldap
########## MySQL ##################
# Dump all grants (permissions), config variables and schema of databases
### dump_mysql_meta [--port=3306]
# Dump all databases in a single compressed file
### dump_mysql_global [--port=3306] [--masterdata]
# Dump each database separately, in a compressed file
### dump_mysql_per_base [--port=3306]
# Dump multiples instances, each in a single compressed file
### dump_mysql_instance [--port=3306]
# Dump each table in schema/data files, for all databases
### dump_mysql_tabs [--port=3306] [--user=foo] [--password=123456789]
########## 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
########## 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
########## Server state ###########
# Run dump-server-state to extract system information
### dump-server-state [any dump-server-state option]
dump_server_state
# Dump file access control lists
### dump_facl
# 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 command
### log "LOCAL_TASKS - start ${dump_file}"
###
### # Execute your dump command
### # Send errors to the error file and the data to the dump file
### my-dump-command 2> "${error_file}" > "${dump_file}"
###
### # Check result and deal with potential errors
### local last_rc=$?
### # shellcheck disable=SC2086
### if [ ${last_rc} -ne 0 ]; then
### log_error "LOCAL_TASKS - 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 command
### log "LOCAL_TASKS - stop ${dump_file}"
### }
########## Optional configuration #####################################
setup_custom() {
# If you set a value (like "linux", "openbsd"…) it will be used,
# Default: uname(1) in lowercase.
### SYSTEM="linux"
# If you set a value it will be used,
# Default: hostname(1).
### HOSTNAME="example-host"
# Email subect 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! ##################################################
main

View file

@ -1,13 +1,8 @@
# 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
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]

View file

@ -15,5 +15,3 @@
#LOGLEVEL=6
#NODE=''
#ARCHIVESDIR='/backup/archives'
#WARNING=48
#CRITICAL=72

View file

@ -11,17 +11,17 @@ Warning: `cp`-ing the files without `-n` or `-i` will replace existing files !
~~~
# git clone https://gitea.evolix.org/evolix/evobackup.git
# cd evobackup
# cp server/bkctld /usr/local/sbin/
# cp bkctld /usr/local/sbin/
# mkdir -p /usr/local/lib/bkctld
# cp server/lib/* /usr/local/lib/bkctld/
# cp lib/* /usr/local/lib/bkctld/
# mkdir -p /usr/local/share/bkctld
# cp server/tpl/* /usr/local/share/bkctld/
# cp server/bkctld.service /lib/systemd/system/
# cp tpl/* /usr/local/share/bkctld/
# cp bkctld.service /lib/systemd/system/
# mkdir -p /usr/local/share/doc/bkctld
# cp client/zzz_evobackup /usr/local/share/doc/bkctld/
# cp zzz_evobackup /usr/local/share/doc/bkctld/
# mkdir -p /usr/local/share/bash_completion/
# cp server/bash_completion /usr/local/share/bash_completion/bkctld
# cp server/bkctld.conf /etc/default/bkctld
# cp bash_completion /usr/local/share/bash_completion/bkctld
# cp bkctld.conf /etc/default/bkctld
~~~
## Chroot dependencies

View file

@ -51,15 +51,15 @@ if dry_run; then
else
mv "${jail_path}" "${new_jail_path}"
fi
if [ -d "${incs_path}" ]; then
if dry_run; then
if dry_run; then
if [ -d "${incs_path}" ]; then
echo "[dry-run] rename ${incs_path} to ${new_incs_path}"
else
fi
else
if [ -d "${incs_path}" ]; then
mv "${incs_path}" "${new_incs_path}"
fi
fi
if [ -d "${jail_config_dir}" ]; then
if dry_run; then
echo "[dry-run] rename ${jail_config_dir} to ${new_jail_config_dir}"

View file

@ -136,7 +136,7 @@ is_btrfs() {
inode=$(stat --format=%i "${path}")
test "$inode" -eq 256
test $inode -eq 256
}
# Returns the list of jails found in the "jails" directory (default)
@ -252,15 +252,15 @@ relative_date() {
new_tmp_file() {
name=${1:-}
mktemp --tmpdir "bkctld.${$}.${name}.XXXXX"
mktemp --tmpdir=/tmp "bkctld.${$}.${name}.XXXXX"
}
new_tmp_dir() {
name=${1:-}
mktemp --directory --tmpdir "bkctld.${$}.${name}.XXXXX"
mktemp --directory --tmpdir=/tmp "bkctld.${$}.${name}.XXXXX"
}
cleanup_tmp() {
find "${TMPDIR:-/tmp}" -name "bkctld.${$}.*" -delete
find /tmp -name "bkctld.${$}.*" -delete
}
new_lock_file() {
lock_file=${1:-}