evobackup/client/lib/dump/misc.sh

560 lines
20 KiB
Bash

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