From b7ce6e1cff5037324841366dde2621e0f9bc06bc Mon Sep 17 00:00:00 2001 From: Jeremy Lecour Date: Fri, 29 Dec 2023 13:50:51 +0100 Subject: [PATCH] Rewrite mysql dump functions and reorganize code --- client/lib/dump-misc.sh | 866 ++++++++++++++++++ client/lib/dump-mysql.sh | 1076 ++++++++++++++++++++++ client/lib/dump-postgresql.sh | 118 +++ client/lib/dump.sh | 1585 --------------------------------- client/lib/main.sh | 24 +- client/zzz_evobackup | 131 ++- 6 files changed, 2175 insertions(+), 1625 deletions(-) create mode 100644 client/lib/dump-misc.sh create mode 100644 client/lib/dump-mysql.sh create mode 100644 client/lib/dump-postgresql.sh delete mode 100644 client/lib/dump.sh diff --git a/client/lib/dump-misc.sh b/client/lib/dump-misc.sh new file mode 100644 index 0000000..e90a1b6 --- /dev/null +++ b/client/lib/dump-misc.sh @@ -0,0 +1,866 @@ +#!/bin/bash +# shellcheck disable=SC2034,SC2317,SC2155 + +####################################################################### +# Dump LDAP files (config, data, all) +# +# Arguments: +####################################################################### +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}" + + 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 - ${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}" + + cp -a "${instance}/dump.rdb" "${dump_dir}/dump.rdb" 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 + + gzip "${dump_dir}/dump.rdb" + + 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: +# --user=[String] (default: ) +# --password=[String] (default: ) +####################################################################### +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}" + 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}" + + 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 - ${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 + ;; + --) + # 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 + + 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 - ${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 MegaCLI configuration +# +# Arguments: +####################################################################### +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}" + 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}" + + 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 - ${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}" + else + log "LOCAL_TASKS - ${FUNCNAME[0]}: 'megacli' not found, unable to dump RAID configuration" + fi +} + + + + + +####################################################################### +# Save some traceroute/mtr results +# +# Arguments: +# --targets=[IP,HOST] (default: ) +####################################################################### +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}" + mkdir -p "${dump_dir}" "${errors_dir}" + # No need to change recursively, the top directory is enough + chmod 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 - ${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 + + 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_server_state_bin} "${options[@]}" + 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: +# +# 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}" + + rabbitmqadmin export "${dump_file}" 2> "${error_file}" >> "${LOGFILE}" + + 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: +####################################################################### +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}" + + 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 - ${FUNCNAME[0]}: 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: ) +# --password=[String] (default: ) +# --repository=[String] (default: snaprepo) +# --snapshot=[String] (default: snapshot.daily) +####################################################################### +dump_elasticsearch_snapshot_singlenode() { + log "LOCAL_TASKS - ${FUNCNAME[0]}: 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 - ${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 + ;; + --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 + 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 + + ## 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 - ${FUNCNAME[0]}: 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: ) +# --password=[String] (default: ) +# --repository=[String] (default: snaprepo) +# --snapshot=[String] (default: snapshot.daily) +# --nfs-server=[IP|HOST] (default: ) +####################################################################### +dump_elasticsearch_snapshot_multinode() { + log "LOCAL_TASKS - ${FUNCNAME[0]}: 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 - ${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 + ;; + --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 + ;; + --nfs-server) + # nfs-server options, with value separated by space + if [ -n "$2" ]; then + option_nfs_server="${2}" + shift + else + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--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 - ${FUNCNAME[0]}: '--nfs-server' 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 + + ## 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 - ${FUNCNAME[0]}: stop dump_elasticsearch_snapshot_multinode" +} diff --git a/client/lib/dump-mysql.sh b/client/lib/dump-mysql.sh new file mode 100644 index 0000000..c9803d8 --- /dev/null +++ b/client/lib/dump-mysql.sh @@ -0,0 +1,1076 @@ +#!/bin/bash +# shellcheck disable=SC2034,SC2317,SC2155 + +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)" +} + +####################################################################### +# Dump complete summary of an instance (using pt-mysql-summary) +# +# Arguments: +# --port=[Integer] (default: ) +# --socket=[String] (default: ) +# --user=[String] (default: ) +# --password=[String] (default: ) +# --defaults-file=[String] (default: ) +# --defaults-extra-file=[String] (default: ) +# --defaults-group-suffix=[String] (default: ) +# --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}" + else + option_dump_label="default" + fi + fi + + local dump_dir="${LOCAL_BACKUP_DIR}/mysql-${option_dump_label}" + local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}") + # DO NOT REMOVE EXISTING DIRECTORIES + # 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) + + pt-mysql-summary "${options[@]}" -- "${connect_options[@]}" 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: ) +# --socket=[String] (default: ) +# --user=[String] (default: ) +# --password=[String] (default: ) +# --defaults-file=[String] (default: ) +# --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}" + else + option_dump_label="default" + fi + fi + + local dump_dir="${LOCAL_BACKUP_DIR}/mysql-${option_dump_label}" + local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}") + # DO NOT REMOVE EXISTING DIRECTORIES + # 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) + + 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 - ${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 +# +# Arguments: +# --masterdata (default: ) +# --port=[Integer] (default: ) +# --socket=[String] (default: ) +# --user=[String] (default: ) +# --password=[String] (default: ) +# --defaults-file=[String] (default: ) +# --defaults-extra-file=[String] (default: ) +# --defaults-group-suffix=[String] (default: ) +# --dump-label=[String] (default: "default") +# used as suffix of the dump dir to differenciate multiple instances +####################################################################### +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="" + # 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 + ;; + --) + # 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_defaults_group_suffix}" ]; then + option_dump_label="${option_defaults_group_suffix}" + elif [ -n "${option_port}" ]; then + option_dump_label="${option_port}" + else + option_dump_label="default" + fi + fi + + 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.gz" + 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 + + ## Global all databases in one 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 + + mysqldump "${connect_options[@]}" "${dump_options[@]}" 2> "${error_file}" | gzip --best > "${dump_file}" + + local last_rc=$? + # shellcheck disable=SC2086 + if [ ${last_rc} -ne 0 ]; then + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: mysqldump 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}" + + ## 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) + + mysqldump "${connect_options[@]}" "${dump_options[@]}" 2> "${error_file}" > "${dump_file}" + + local last_rc=$? + # shellcheck disable=SC2086 + if [ ${last_rc} -ne 0 ]; then + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: mysqldump 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}" +} + +####################################################################### +# Dump a compressed file per database of an instance +# +# Arguments: +# --port=[Integer] (default: ) +# --socket=[String] (default: ) +# --user=[String] (default: ) +# --password=[String] (default: ) +# --defaults-file=[String] (default: ) +# --defaults-extra-file=[String] (default: ) +# --defaults-group-suffix=[String] (default: ) +# --dump-label=[String] (default: "default") +# used as suffix of the dump dir to differenciate multiple instances +####################################################################### +dump_mysql_per_base() { + local option_port="" + local option_socket="" + local option_dump_label="" + # 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 + ;; + --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 + ;; + --) + # 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 + + option_dump_label="${option_dump_label:-default}" + + declare -a connect_options + connect_options=() + if [ -n "${option_port}" ]; then + connect_options+=(--port="${option_port}") + fi + if [ -n "${option_socket}" ]; then + connect_options+=(--protocol=socket) + connect_options+=(--socket="${option_socket}") + fi + + local dump_dir="${LOCAL_BACKUP_DIR}/mysql-${option_dump_label}/databases" + 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}" + + declare -a options + options=() + options+=(--defaults-extra-file=/etc/mysql/debian.cnf) + options+=(--force) + options+=(--events) + options+=(--hex-blob) + + databases=$(mysql_list_databases "${connect_options[@]}") + + for database in ${databases}; do + local error_file="${errors_dir}/${database}.err" + local dump_file="${dump_dir}/${database}.sql.gz" + log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}" + + mysqldump "${connect_options[@]}" "${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 - ${FUNCNAME[0]}: mysqldump 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 + + ## Schema only (no data) for each databases + for database in ${databases}; do + 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 options + options=() + options+=(--defaults-extra-file=/etc/mysql/debian.cnf) + options+=(--force) + options+=(--no-data) + options+=(--databases "${database}") + + mysqldump "${connect_options[@]}" "${options[@]}" 2> "${error_file}" > "${dump_file}" + + local last_rc=$? + # shellcheck disable=SC2086 + if [ ${last_rc} -ne 0 ]; then + log_error "LOCAL_TASKS - ${FUNCNAME[0]}: mysqldump 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 + + ## Grants and summary + if [ -n "${option_port}" ]; then + dump_mysql_grants --port="${option_port}" + dump_mysql_summary --port="${option_port}" + elif [ -n "${option_socket}" ]; then + dump_mysql_grants --socket="${option_socket}" + dump_mysql_summary --socket="${option_socket}" + else + dump_mysql_grants + dump_mysql_summary + fi +} + +####################################################################### +# Dump "tabs style" separate schema/data for each database of an instance +# +# Arguments: +# --masterdata (default: ) +# --port=[Integer] (default: ) +# --socket=[String] (default: ) +# --user=[String] (default: ) +# --password=[String] (default: ) +# --defaults-file=[String] (default: ) +# --defaults-extra-file=[String] (default: ) +# --defaults-group-suffix=[String] (default: ) +# --dump-label=[String] (default: "default") +# used as suffix of the dump dir to differenciate multiple instances +####################################################################### +dump_mysql_tabs() { + local option_port="" + local option_socket="" + local option_dump_label="" + # 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 + ;; + --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 + ;; + --) + # 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 + + option_dump_label="${option_dump_label:-default}" + + databases=$(mysql_list_databases "${option_port}") + + 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 options + options=() + options+=(--defaults-extra-file=/etc/mysql/debian.cnf) + if [ -n "${option_port}" ]; then + options+=(--port="${option_port}") + fi + if [ -n "${option_socket}" ]; then + options+=(--protocol=socket) + options+=(--socket="${option_socket}") + fi + 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 - ${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 +} diff --git a/client/lib/dump-postgresql.sh b/client/lib/dump-postgresql.sh new file mode 100644 index 0000000..ebbe7d6 --- /dev/null +++ b/client/lib/dump-postgresql.sh @@ -0,0 +1,118 @@ +#!/bin/bash +# shellcheck disable=SC2034,SC2317,SC2155 + +####################################################################### +# Dump a single file of all PostgreSQL databases +# +# Arguments: +####################################################################### +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}" + 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.gz" + log "LOCAL_TASKS - ${FUNCNAME[0]}: 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 - ${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: +####################################################################### +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}" + 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.gz" + log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}" + + (sudo -u postgres /usr/bin/pg_dump --create -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 - ${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: +# +# 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}" +} diff --git a/client/lib/dump.sh b/client/lib/dump.sh deleted file mode 100644 index b099fe2..0000000 --- a/client/lib/dump.sh +++ /dev/null @@ -1,1585 +0,0 @@ -#!/bin/bash -# shellcheck disable=SC2034,SC2317,SC2155 - -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: -####################################################################### -dump_ldap() { - ## OpenLDAP : example with slapcat - local dump_dir="${LOCAL_BACKUP_DIR}/ldap" - rm -rf "${dump_dir}" - mkdir -p "${dump_dir}" - chmod -R 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 config variables of an instance -# -# Arguments: -# --port=[Integer] (default: 3306) -####################################################################### -dump_mysql_variables() { - 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 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}" - rmdir --ignore-fail-on-non-empty "${errors_dir}" - fi - log "LOCAL_TASKS - stop ${dump_file}" -} - -####################################################################### -# Dump grants of an instance -# -# Arguments: -# --port=[Integer] (default: 3306) -####################################################################### -dump_mysql_grants() { - 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) - 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 - 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}" - rmdir --ignore-fail-on-non-empty "${errors_dir}" - fi - log "LOCAL_TASKS - stop ${dump_file}" - else - log "LOCAL_TASKS - pt-show-grants not found, unable to dump grants" - fi -} - - - -####################################################################### -# Dump a single compressed file of all databases of an instance -# -# Arguments: -# --masterdata (default: ) -# --port=[Integer] (default: 3306) -####################################################################### -dump_mysql_global() { - 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 - - local dump_dir="${LOCAL_BACKUP_DIR}/mysql-global-${option_port}" - local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}") - rm -rf "${dump_dir}" "${errors_dir}" - mkdir -p "${dump_dir}" "${errors_dir}" - chmod -R 700 "${dump_dir}" "${errors_dir}" - - local error_file="${errors_dir}/mysqldump.err" - local dump_file="${dump_dir}/mysqldump.sql.gz" - log "LOCAL_TASKS - start ${dump_file}" - - ## Global all databases in one file - 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}" - - ## 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 - 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+=(--all-databases) - - 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}" - rmdir --ignore-fail-on-non-empty "${errors_dir}" - fi - log "LOCAL_TASKS - stop ${dump_file}" - - dump_mysql_grants --port="${option_port}" - - dump_mysql_variables --port="${option_port}" -} - -####################################################################### -# Dump a compressed file per database of an instance -# -# Arguments: -# --port=[Integer] (default: 3306) -####################################################################### -dump_mysql_per_base() { - 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 - - local dump_dir="${LOCAL_BACKUP_DIR}/mysql-per-base-${option_port}" - local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}") - rm -rf "${dump_dir}" "${errors_dir}" - mkdir -p "${dump_dir}" "${errors_dir}" - chmod -R 700 "${dump_dir}" "${errors_dir}" - - 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 - - ## 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_mysql_grants --port="${option_port}" - - dump_mysql_variables --port="${option_port}" -} - -####################################################################### -# Dump "tabs style" separate schema/data for each database of an instance -# -# Arguments: -# --port=[Integer] (default: 3306) -####################################################################### -dump_mysql_tabs() { - 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 - - databases=$(mysql_list_databases "${option_port}") - for database in ${databases}; do - local dump_dir="${LOCAL_BACKUP_DIR}/mysql-tabs-${option_port}/${database}" - local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}") - rm -rf "${dump_dir}" "${errors_dir}" - mkdir -p "${dump_dir}" "${errors_dir}" - chmod -R 700 "${dump_dir}" "${errors_dir}" - chown -RL mysql "${dump_dir}" - - local error_file="${errors_dir}.err" - log "LOCAL_TASKS - start ${dump_dir}" - - 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}" - rmdir --ignore-fail-on-non-empty "${errors_dir}" - 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: ) -# --password=[String] (default: ) -####################################################################### -dump_mysql_instance() { - 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 - - local dump_dir="${LOCAL_BACKUP_DIR}/mysql-instance-${option_port}" - local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}") - rm -rf "${dump_dir}" "${errors_dir}" - mkdir -p "${dump_dir}" "${errors_dir}" - chmod -R 700 "${dump_dir}" "${errors_dir}" - - 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}/mysql-global.err" - local dump_file="${dump_dir}/mysql-global.sql.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}" - rmdir --ignore-fail-on-non-empty "${errors_dir}" - fi - log "LOCAL_TASKS - stop ${dump_file}" -} - -####################################################################### -# Dump a single file of all PostgreSQL databases -# -# Arguments: -####################################################################### -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}" - mkdir -p "${dump_dir}" "${errors_dir}" - chmod -R 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.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 error_file="${errors_dir}/pg_dumpall.err" - # local dump_file="${dump_dir}/pg_dumpall.sql" - # 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: -####################################################################### -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}" - mkdir -p "${dump_dir}" "${errors_dir}" - chmod -R 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 -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}" - rmdir --ignore-fail-on-non-empty "${errors_dir}" - fi - log "LOCAL_TASKS - stop ${dump_file}" - done - ) -} - -####################################################################### -# Dump a compressed file per database -# -# Arguments: -# -# 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}" - chmod -R 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}" - rmdir --ignore-fail-on-non-empty "${errors_dir}" - 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 -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 - '--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}" - mkdir -p "${dump_dir}" "${errors_dir}" - chmod -R 700 "${dump_dir}" "${errors_dir}" - - if [ -f "${instance}/dump.rdb" ]; then - local error_file="${errors_dir}/${name}.err" - log "LOCAL_TASKS - start ${dump_dir}" - - cp -a "${instance}/dump.rdb" "${dump_dir}/dump.rdb" 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}" - rmdir --ignore-fail-on-non-empty "${errors_dir}" - fi - - gzip "${dump_dir}/dump.rdb" - - local last_rc=$? - # shellcheck disable=SC2086 - if [ ${last_rc} -ne 0 ]; then - log_error "LOCAL_TASKS - gzip ${dump_dir}/dump.rdb returned an error ${last_rc}" "${error_file}" - GLOBAL_RC=${E_DUMPFAILED} - else - rm -f "${error_file}" - rmdir --ignore-fail-on-non-empty "${errors_dir}" - 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: ) -# --password=[String] (default: ) -####################################################################### -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}" - mkdir -p "${dump_dir}" "${errors_dir}" - chmod -R 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}" - rmdir --ignore-fail-on-non-empty "${errors_dir}" - fi - log "LOCAL_TASKS - stop ${dump_dir}" -} - -####################################################################### -# Dump MegaCLI configuration -# -# Arguments: -####################################################################### -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}" - mkdir -p "${dump_dir}" "${errors_dir}" - chmod -R 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}" - rmdir --ignore-fail-on-non-empty "${errors_dir}" - fi - log "LOCAL_TASKS - stop ${dump_file}" -} - -####################################################################### -# Save some traceroute/mtr results -# -# Arguments: -# --targets=[IP,HOST] (default: ) -####################################################################### -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}" - mkdir -p "${dump_dir}" "${errors_dir}" - chmod -R 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 - # 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: -# -# 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}" - chmod -R 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}" - rmdir --ignore-fail-on-non-empty "${errors_dir}" - fi - log "LOCAL_TASKS - stop ${dump_file}" -} - -####################################################################### -# Save Files ACL on various partitions. -# -# Arguments: -####################################################################### -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}" - chmod -R 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: ) -# --password=[String] (default: ) -# --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: ) -# --password=[String] (default: ) -# --repository=[String] (default: snaprepo) -# --snapshot=[String] (default: snapshot.daily) -# --nfs-server=[IP|HOST] (default: ) -####################################################################### -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" -} diff --git a/client/lib/main.sh b/client/lib/main.sh index be0ff1e..4d61839 100644 --- a/client/lib/main.sh +++ b/client/lib/main.sh @@ -14,7 +14,9 @@ set -u set -o pipefail source "${LIBDIR}/utilities.sh" -source "${LIBDIR}/dump.sh" +source "${LIBDIR}/dump-mysql.sh" +source "${LIBDIR}/dump-postgresql.sh" +source "${LIBDIR}/dump-misc.sh" # Called from main, it is wrapping the local_tasks function defined in the real script local_tasks_wrapper() { @@ -184,7 +186,7 @@ sync() { rsync_server=$(echo "${server}" | cut -d':' -f1) rsync_port=$(echo "${server}" | cut -d':' -f2) - log "START SYNC_TASKS - \"${sync_name}\" : server=${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" @@ -208,7 +210,7 @@ sync() { if [ -n "${mtree_bin}" ]; then # Dump filesystem stats with mtree - log "SYNC_TASKS - start mtree" + log "SYNC_TASKS - ${sync_name}: start mtree" # Loop over Rsync includes for i in "${!rsync_includes[@]}"; do @@ -232,15 +234,15 @@ sync() { done if [ "${#mtree_files[@]}" -le 0 ]; then - log_error "SYNC_TASKS - ERROR: mtree didn't produce any file" + log_error "SYNC_TASKS - ${sync_name}: ERROR: mtree didn't produce any file" fi - log "SYNC_TASKS - stop mtree (files: ${mtree_files[*]})" + log "SYNC_TASKS - ${sync_name}: stop mtree (files: ${mtree_files[*]})" else - log "SYNC_TASKS - skip mtree (missing)" + log "SYNC_TASKS - ${sync_name}: skip mtree (missing)" fi else - log "SYNC_TASKS - skip mtree (disabled)" + log "SYNC_TASKS - ${sync_name}: skip mtree (disabled)" fi rsync_bin=$(command -v rsync) @@ -274,7 +276,7 @@ sync() { rsync_main_args+=("root@${rsync_server}:${REMOTE_BACKUP_DIR}/") # … log it - log "SYNC_TASKS - \"${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[@]}" @@ -288,7 +290,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_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 @@ -317,13 +319,13 @@ sync() { rsync_report_args+=("root@${rsync_server}:${REMOTE_LOG_DIR}/") # … log it - log "SYNC_TASKS - ${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_name} server=${server}" + log "STOP SYNC_TASKS - ${sync_name}: server=${server}" } setup() { diff --git a/client/zzz_evobackup b/client/zzz_evobackup index ae0e4ef..ea85983 100644 --- a/client/zzz_evobackup +++ b/client/zzz_evobackup @@ -125,7 +125,7 @@ sync_tasks() { # 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) +# (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. @@ -134,42 +134,111 @@ sync_tasks() { local_tasks() { - ########## OpenLDAP ############### + ########## Server state ########### - ### dump_ldap + # Run dump-server-state to extract system information + # + # Options : any dump-server-state option (see 'dump-server-state -h') + # + dump_server_state ########## MySQL ################## + # Very common strategy for a single instance server with default configuration : + # + ### dump_mysql_global; dump_mysql_grants; dump_mysql_summary + # Dump all databases in a single compressed file - # and all grants (permissions), config variables and schema of databases - ### dump_mysql_global [--port=3306] [--masterdata] + # + # Options : + # --masterdata (default: ) + # --port=[Integer] (default: ) + # --socket=[String] (default: ) + # --user=[String] (default: ) + # --password=[String] (default: ) + # --defaults-file=[String] (default: ) + # --defaults-extra-file=[String] (default: ) + # --defaults-group-suffix=[String] (default: ) + # --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 - # and all grants (permissions), config variables and schema of databases - ### dump_mysql_per_base [--port=3306] - - # Dump a specific instance, in a single compressed file - # This is similar to "dump_mysql_global", but will custom authentication - ### dump_mysql_instance [--port=3306] [--user=foo] [--password=123456789] + # + # Options : + # --port=[Integer] (default: ) + # --socket=[String] (default: ) + # --user=[String] (default: ) + # --password=[String] (default: ) + # --defaults-file=[String] (default: ) + # --defaults-extra-file=[String] (default: ) + # --defaults-group-suffix=[String] (default: ) + # --dump-label=[String] (default: "default") + # used as suffix of the dump dir to differenciate multiple instances + # + ### dump_mysql_per_base - # Dump only grants of an instance - ### dump_mysql_grants [--port=3306] + # Dump permissions of an instance (using pt-show-grants) + # + # Options : + # --port=[Integer] (default: ) + # --socket=[String] (default: ) + # --user=[String] (default: ) + # --password=[String] (default: ) + # --defaults-file=[String] (default: ) + # --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 only variables of an instance - ### dump_mysql_variables [--port=3306] + # Dump complete summary of an instance (using pt-mysql-summary) + # + # Options : + # --port=[Integer] (default: ) + # --socket=[String] (default: ) + # --user=[String] (default: ) + # --password=[String] (default: ) + # --defaults-file=[String] (default: ) + # --defaults-extra-file=[String] (default: ) + # --defaults-group-suffix=[String] (default: ) + # --dump-label=[String] (default: "default") + # used as suffix of the dump dir to differenciate multiple instances + # + ### dump_mysql_summary - # Dump each table in schema/data files - ### dump_mysql_tabs [--port=3306] [--user=foo] [--password=123456789] + # Dump each table in separate schema/data files + # + # Options : + # --port=[Integer] (default: ) + # --socket=[String] (default: ) + # --user=[String] (default: ) + # --password=[String] (default: ) + # --defaults-file=[String] (default: ) + # --defaults-extra-file=[String] (default: ) + # --defaults-group-suffix=[String] (default: ) + # --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 ################ @@ -179,14 +248,17 @@ local_tasks() { ########## Redis ################## # Copy data file for all instances + # ### dump_redis [--instances=] ########## 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 ############### @@ -196,23 +268,24 @@ local_tasks() { ########## 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 - ########## 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 : } @@ -232,7 +305,7 @@ local_tasks() { ### mkdir -p -m 700 "${dump_dir}" "${errors_dir}" ### ### # Log the start of the command -### log "LOCAL_TASKS - start ${dump_file}" +### log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}" ### ### # Execute your dump command ### # Send errors to the error file and the data to the dump file @@ -242,14 +315,14 @@ local_tasks() { ### 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}" +### 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 command -### log "LOCAL_TASKS - stop ${dump_file}" +### log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}" ### } ########## Optional configuration #####################################