From: Frank Brehm Date: Thu, 16 Nov 2017 14:50:05 +0000 (+0100) Subject: Adding bin/backup-pdns-global.sh X-Git-Tag: 0.1.2~73 X-Git-Url: https://git.uhu-banane.net/?a=commitdiff_plain;h=25fd24d217cfdfd684c2c8b7ea5f199e422302d7;p=pixelpark%2Fadmin-tools.git Adding bin/backup-pdns-global.sh --- diff --git a/bin/backup-pdns-global.sh b/bin/backup-pdns-global.sh new file mode 100755 index 0000000..ed711a6 --- /dev/null +++ b/bin/backup-pdns-global.sh @@ -0,0 +1,491 @@ +#!/usr/bin/env bash + +set -e +set -u + +export LC_ALL=C +export LANG=C + +VERBOSE="n" +DEBUG="n" +QUIET='n' + +VERSION="1.0" + +# console colors: +RED="" +YELLOW="" +GREEN="" +BLUE="" +NORMAL="" + +HAS_TTY='y' + +BASENAME="$(basename ${0})" +BASE_DIR="$(dirname ${0})" + +declare -a DATABASES=() + +######################################################### +# Modify below variables to fit your need ---- +######################################################### + +# Where to store backup copies. +BACKUP_ROOTDIR="/var/backup/pdns" + +KEEP_DAYS=30 + +# Date. +YEAR="$( date +%Y)" +MONTH="$( date +%m)" +DAY="$( date +%d)" +TIME="$( date +%H:%M:%S)" +TIMESTAMP="${YEAR}-${MONTH}-${DAY}-${TIME}" + +# Define, check, create directories. +BACKUP_DIR="${BACKUP_ROOTDIR}/${YEAR}/${MONTH}/${DAY}" + +#------------------------------------------------------------------- +detect_color() { + + local safe_term="${TERM//[^[:alnum:]]/?}" + local match_lhs="" + local use_color="false" + [[ -f ~/.dir_colors ]] && match_lhs="${match_lhs}$(<~/.dir_colors)" + [[ -f /etc/DIR_COLORS ]] && match_lhs="${match_lhs}$(/dev/null \ + && match_lhs=$(dircolors --print-database) + [[ $'\n'${match_lhs} == *$'\n'"TERM "${safe_term}* ]] && use_color="true" + + # console colors: + if [ "${use_color}" = "true" ] ; then + RED="\033[38;5;196m" + YELLOW="\033[38;5;226m" + GREEN="\033[38;5;46m" + BLUE="\033[38;5;27m" + NORMAL="\033[39m" + else + RED="" + YELLOW="" + GREEN="" + BLUE="" + NORMAL="" + fi + + local my_tty=$(tty) + if [[ "${my_tty}" =~ 'not a tty' ]] ; then + my_tty='-' + fi + + if [[ "${my_tty}" = '-' || "${safe_term}" = "dump" ]] ; then + HAS_TTY='n' + fi + +} +detect_color + +#------------------------------------------------------------------------------ +description() { + echo -e $( cat <<-EOF + Creates a backup of all zones of the global instance of PowerDNS + on the current host. + + Only the user '${GREEN}root${NORMAL}' may execute this script. + + EOF + ) +} + +#------------------------------------------------------------------------------ +usage() { + cat <<-EOF + Usage: ${BASENAME} [-K DAYS|--keep=DAYS] [-d|--debug] [[-v|--verbose] | [-q|--quiet]]] [--nocolor] + ${BASENAME} [-h|--help] + ${BASENAME} [-V|--version] + + Options: + -K|--keep DAYS Keep the backup files of the last DAYS. Default: ${KEEP_DAYS} days. + -d|--debug Debug output (bash -x). + -v|--verbose Set verbosity on. Mutually exclusive to '--quiet'. + -q|--quiet Quiet execution, only errors and warnings are shown. + --nocolor Don't use colors on display. + -h|--help Show this output and exit. + -V|--version prints out version number of the script and exit + EOF +} + + +#------------------------------------------------------------------------------ +get_options() { + + local tmp= + local base_dir= + + set +e + tmp=$( getopt -o K:dvqhV \ + --long keep:,debug,verbose,quiet,nocolor,help,version \ + -n "${BASENAME}" -- "$@" ) + if [[ $? != 0 ]] ; then + echo "" >&2 + usage >&2 + exit 1 + fi + set -e + + # Note the quotes around `$TEMP': they are essential! + eval set -- "${tmp}" + + local p= + + while true ; do + case "$1" in + -K|--keep) + KEEP_DAYS="$2" + shift + shift + ;; + -d|--debug) + DEBUG="y" + shift + ;; + -v|--verbose) + VERBOSE="y" + shift + ;; + -q|--quiet) + QUIET="y" + RED="" + YELLOW="" + GREEN="" + BLUE="" + NORMAL="" + shift + ;; + --nocolor) + RED="" + YELLOW="" + GREEN="" + BLUE="" + NORMAL="" + shift + ;; + -h|--help) + description + echo + usage + exit 0 + ;; + -V|--version) + echo "${BASENAME} version: ${VERSION}" + exit 0 + ;; + --) shift + break + ;; + *) echo "Internal error!" + exit 1 + ;; + esac + done + + if [[ "${DEBUG}" = "y" ]] ; then + set -x + fi + if [[ "${VERBOSE}" == "y" && "${QUIET}" == "y" ]] ; then + error "The parameters '${RED}${VERBOSE}${NORMAL}' and '${RED}${VERBOSE}${NORMAL}' are mutually exclusive." + usage >&2 + exit 1 + fi + + local keep_int=$(( $KEEP_DAYS + 0 )) + if [[ "${keep_int}" -le "0" ]] ; then + error "Invalid number of days '${RED}${KEEP_DAYS}${NORMAL}' to keep backup files." + echo >&2 + description >&2 + echo + usage >&2 + exit 1 + fi + debug "Keeping backupfiles, which are not older than ${keep_int} days." + KEEP_DAYS="${keep_int}" + + local cur_user=$( id -u -n ) + local cur_uid=$( id -u ) + if [[ "${cur_uid}" != "0" ]] ; then + error "Wrong user '${RED}${cur_user}${NORMAL}'." + echo >&2 + description >&2 + echo + usage >&2 + exit 1 + fi + +} + +######################################### +# Some often used funktions + +#------------------------------------------------------------------------------ +my_date() { + date +'%F %T.%N %:::z' +} + +#------------------------------------------------------------------------------ +debug() { + if [[ "${VERBOSE}" != "y" ]] ; then + return 0 + fi + echo -e " * [$(my_date)] [${BASENAME}:DEBUG]: $@" +} + +#------------------------------------------------------------------------------ +info() { + if [[ "${QUIET}" == "y" ]] ; then + return 0 + fi + echo -e " ${GREEN}*${NORMAL} [$(my_date)] [${BASENAME}:${GREEN}INFO${NORMAL}] : $@" +} + +#------------------------------------------------------------------------------ +warn() { + echo -e " ${YELLOW}*${NORMAL} [$(my_date)] [${BASENAME}:${YELLOW}WARN${NORMAL}] : $@" >&2 +} + +#------------------------------------------------------------------------------ +error() { + echo -e " ${RED}*${NORMAL} [$(my_date)] [${BASENAME}:${RED}ERROR${NORMAL}]: $@" >&2 +} + +#------------------------------------------------------------------------------ +MKDIR() { + local cmd="mkdir" + if [[ "${VERBOSE}" == "y" ]] ; then + cmd+=" --verbose" + fi + eval ${cmd} "$@" +} + +#------------------------------------------------------------------------------ +RM() { + local cmd="rm" + if [[ "${VERBOSE}" == "y" ]] ; then + cmd+=" --verbose" + fi + eval ${cmd} "$@" +} + +#------------------------------------------------------------------------------ +MV() { + local cmd="mv" + if [[ "${VERBOSE}" == "y" ]] ; then + cmd+=" --verbose" + fi + eval ${cmd} "$@" +} + +#------------------------------------------------------------------------------ +RMDIR() { + local cmd="rmdir" + if [[ "${VERBOSE}" == "y" ]] ; then + cmd+=" --verbose" + fi + eval ${cmd} "$@" +} + +#------------------------------------------------------------------------------ +empty_line() { + if [[ "${QUIET}" == "y" ]] ; then + echo >> "${LOGFILE}" + return 0 + fi + echo +} + +################################################################################ + +prepare_dirs() { + + debug "Changing to '${BASE_DIR}' ..." + cd "${BASE_DIR}" + + if [[ ! -d "${BACKUP_ROOTDIR}" ]] ; then + error "Directory '${RED}${BACKUP_ROOTDIR}${NORMAL}' does not exists or is not a directory." + exit 5 + fi + if [[ ! -w "${BACKUP_ROOTDIR}" ]] ; then + error "No write access to '${RED}${BACKUP_ROOTDIR}${NORMAL}'." + exit 6 + fi + + info "Creating all necessary directories ..." + MKDIR -p "${BACKUP_DIR}" + + local i=0 + local new_backup_dir="${BACKUP_DIR}/"$( printf "%03d" "$i" ) + while [[ -d "${new_backup_dir}" ]] ; do + i=$(( $i + 1 )) + new_backup_dir="${BACKUP_DIR}/"$( printf "%03d" "$i" ) + done + BACKUP_DIR="${new_backup_dir}" + MKDIR -p "${BACKUP_DIR}" + +} + +#------------------------------------------------------------------------------ +cleanup_old_backups() { + + info "Cleaning up old backup files and directories ..." + + local verbose_option="" + if [[ "${VERBOSE}" == "y" ]] ; then + verbose_option="--verbose" + fi + + find "${BACKUP_ROOTDIR}" -type f -mtime +${KEEP_DAYS} -print0 | \ + xargs --null --no-run-if-empty rm ${verbose_option} + + local year= + local month= + local day= + local i= + + for year in $( ls -1 "${BACKUP_ROOTDIR}" ); do + local y_dir="${BACKUP_ROOTDIR}/${year}" + if [[ -d "${y_dir}" ]] ; then + for month in $( ls -1 "${y_dir}" ); do + local m_dir="${y_dir}/${month}" + if [[ -d "${m_dir}" ]] ; then + for day in $( ls -1 "${m_dir}" ); do + local d_dir="${m_dir}/${day}" + if [[ -d "${d_dir}" ]] ; then + for i in $( ls -1 "${d_dir}" ); do + local i_dir="${d_dir}/${i}" + if [[ -d "${i_dir}" && "${i_dir}" != "${BACKUP_DIR}" ]] ; then + rmdir --ignore-fail-on-non-empty "${i_dir}" + if [[ ! -d "${i_dir}" ]] ; then + debug "Removed directory '${i_dir}'." + fi + fi + done + rmdir --ignore-fail-on-non-empty "${d_dir}" + if [[ ! -d "${d_dir}" ]] ; then + debug "Removed directory '${d_dir}'." + fi + fi + done + rmdir --ignore-fail-on-non-empty "${m_dir}" + if [[ ! -d "${m_dir}" ]] ; then + debug "Removed directory '${m_dir}'." + fi + fi + done + rmdir --ignore-fail-on-non-empty "${y_dir}" + if [[ ! -d "${y_dir}" ]] ; then + debug "Removed directory '${y_dir}'." + fi + fi + done + +} + +#------------------------------------------------------------------------------ +backup_databases() { + + local db= + for db in "${DATABASES[@]}" ; do + backup_database "${db}" + done + + empty_line + local k_bytes=$(( ${BYTES_TOTAL} / 1024 )) + local m_bytes=$(( ${k_bytes} / 1024 )) + local msg=$( printf "Total compressed size: %10d Bytes => %7d KiB => %4d MiB" \ + "${BYTES_TOTAL}" "${k_bytes}" "${m_bytes}" ) + info "${msg}" +} + +#------------------------------------------------------------------------------ +backup_database() { + + local db="$1" + + empty_line + info "Backing up database '${GREEN}${db}${NORMAL}' ..." + + local output_sql="${db}-${TIMESTAMP}.sql" + local output_sql_compressed="${output_sql}.bz2" + local out_sql_tmp="${TMP_DIR}/${output_sql}" + local out_sql_tmp_compressed="${TMP_DIR}/${output_sql_compressed}" + local out_sql_tgt="${BACKUP_DIR}/${output_sql}" + local out_sql_tgt_compressed="${BACKUP_DIR}/${output_sql_compressed}" + + local verbose_option="" + if [[ "${VERBOSE}" == "y" ]] ; then + verbose_option="--verbose" + fi + + pg_dump ${verbose_option} --blobs --clean \ + --create --if-exists --serializable-deferrable \ + "${db}" 2>&1 >"${out_sql_tmp}" | tee -a "${LOGFILE}" + + local blocks=$(stat -c "%b" "${out_sql_tmp}") + local bs=$(stat -c "%B" "${out_sql_tmp}") + local bytes=$(stat -c "%s" "${out_sql_tmp}") + local b_bytes=$(( ${blocks} * ${bs} )) + local k_bytes=$(( ${b_bytes} / 1024 )) + local m_bytes=$(( ${k_bytes} / 1024 )) + local msg=$( printf "Original size of %-50s %10d Bytes => %7d KiB => %4d MiB" \ + "'${output_sql}':" "${bytes}" "${k_bytes}" "${m_bytes}" ) + info "${msg}" + + debug "Compressing '${out_sql_tmp}' ..." + bzip2 ${verbose_option} --best "${out_sql_tmp}" 2>&1 | tee -a "${LOGFILE}" + + blocks=$(stat -c "%b" "${out_sql_tmp_compressed}") + bs=$(stat -c "%B" "${out_sql_tmp_compressed}") + bytes=$(stat -c "%s" "${out_sql_tmp_compressed}") + b_bytes=$(( ${blocks} * ${bs} )) + k_bytes=$(( ${b_bytes} / 1024 )) + m_bytes=$(( ${k_bytes} / 1024 )) + + BYTES_TOTAL=$(( ${BYTES_TOTAL} + ${b_bytes} )) + + local msg=$( printf "Compressed size of %-50s %10d Bytes => %7d KiB => %4d MiB" \ + "'${output_sql}':" "${bytes}" "${k_bytes}" "${m_bytes}" ) + info "${msg}" + + debug "Moving '${out_sql_tmp_compressed}' => '${BACKUP_DIR}' ..." + MV -i "${out_sql_tmp_compressed}" "${BACKUP_DIR}" + +} + + + +################################################################################ +## +## Main +## +################################################################################ + +#------------------------------------------------------------------------------ +main() { + + get_options "$@" + + prepare_dirs + info "Starting backup ..." + cleanup_old_backups + #get_databases + #backup_databases + + empty_line + info "Finished." + +} + +main "$@" + +exit 0 + +# vim: ts=4 et list