]> Frank Brehm's Git Trees - pixelpark/pp-admin-tools.git/commitdiff
Adding scripts/backup-pdns.sh
authorFrank Brehm <frank.brehm@pixelpark.com>
Tue, 21 Dec 2021 16:46:33 +0000 (17:46 +0100)
committerFrank Brehm <frank.brehm@pixelpark.com>
Tue, 21 Dec 2021 16:46:33 +0000 (17:46 +0100)
scripts/backup-pdns.sh [new file with mode: 0755]

diff --git a/scripts/backup-pdns.sh b/scripts/backup-pdns.sh
new file mode 100755 (executable)
index 0000000..1004243
--- /dev/null
@@ -0,0 +1,485 @@
+#!/usr/bin/env bash
+
+set -e
+set -u
+
+BASE_NAME="$( basename ${0} )"
+MY_REAL_NAME=$( readlink -f $0 )
+BIN_DIR=$( dirname "${MY_REAL_NAME}" )
+BASE_DIR=$( dirname "${BIN_DIR}" )
+LIB_DIR="${BASE_DIR}/lib"
+CONF_DIR="${BASE_DIR}/etc"
+
+if [[ -f "${LIB_DIR}/functions.rc" ]] ; then
+    . "${LIB_DIR}/functions.rc"
+else
+    echo "Bash resource file '${LIB_DIR}/functions.rc' not found" >&2
+    exit 5
+fi
+
+export LC_ALL="en_US.utf8"
+export LANG="en_US.utf8"
+
+VERSION="1.3"
+
+declare -a ZONES=()
+KEEP_LAST=3
+KEEP_DAYS=15
+KEEP_WEEKS=12
+KEEP_MONTHS=18
+KEEP_YEARS=4
+# Where to store backup copies.
+BACKUP_ROOTDIR="/var/backup/pdns"
+
+BACKUP_TIMESTAMP="$( date +'%Y-%m-%d_%H-%M-%S' )"
+
+# Define, check, create directories.
+BACKUP_DIR=
+
+BYTES_TOTAL=0
+
+detect_color
+
+DESCRIPTION=$( 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: ${BASE_NAME} [Common Options] [-L LAST] [-D DAYS] [-W WEEKS] [-M MONTHS] [-Y YEARS] [--dir DIRECTORY]
+              ${BASE_NAME} [-h|--help]
+              ${BASE_NAME} [-V|--version]
+
+           Options:
+               -L|--last LAST  Keep the last ${KEEP_LAST} backup sets.
+               -D|--days DAYS  Keep the backup files of the last DAYS. Default: ${KEEP_DAYS} days.
+               -W|--weeks WEEKS
+                               Keep the backup files of the last WEEKS. Default: ${KEEP_WEEKS} weeks.
+               -M|--months MONTHS
+                               Keep the backup files of the last MONTHS. Default: ${KEEP_MONTHS} months.
+               -Y|--years YEARS
+                               Keep the backup files of the last YEARS. Default: ${KEEP_YEARS} years.
+               --dir DIRECTORY The directory, where to keep the backup files. Default: '${BACKUP_ROOTDIR}'."
+       EOF
+
+    echo
+    echo "    Common Options:"
+    echo "${STD_USAGE_MSG}"
+
+}
+
+#------------------------------------------------------------------------------
+get_options() {
+
+    local tmp=
+    local base_dir=
+
+    local short_options="L:D:W:M:Y:${STD_SHORT_OPTIONS}"
+    local long_options="last:,days:,weeks:,months:,years:,dir:,${STD_LONG_OPTIONS}"
+
+    set +e
+    tmp=$( getopt -o "${short_options}" --long "${long_options}" -n "${BASE_NAME}" -- "$@" )
+    if [[ $? != 0 ]] ; then
+        echo "" >&2
+        usage >&2
+        exit 1
+    fi
+    set -e
+
+    # Note the quotes around `$TEMP': they are essential!
+    eval set -- "${tmp}"
+    eval_common_options "$@"
+    if [[ "${DEBUG}" == 'y' ]] ; then
+        declare -p REMAINING_OPTS
+        declare -p REMAINING_ARGS
+    fi
+
+    local len="${#REMAINING_OPTS[*]}"
+    local i="0"
+    local j=
+    local arg=
+    while [[ "$i" -lt "${len}" ]] ; do
+
+        arg="${REMAINING_OPTS[$i]}"
+
+        case "${arg}" in
+            -L|--last)
+                j=$(( $i + 1 ))
+                KEEP_LAST="${REMAINING_OPTS[$j]}"
+                i=$(( $i + 2 ))
+                ;;
+            -D|--days)
+                j=$(( $i + 1 ))
+                KEEP_DAYS="${REMAINING_OPTS[$j]}"
+                i=$(( $i + 2 ))
+                ;;
+            -W|--weeks)
+                j=$(( $i + 1 ))
+                KEEP_WEEKS="${REMAINING_OPTS[$j]}"
+                i=$(( $i + 2 ))
+                ;;
+            -M|--months)
+                j=$(( $i + 1 ))
+                KEEP_MONTHS="${REMAINING_OPTS[$j]}"
+                i=$(( $i + 2 ))
+                ;;
+            -Y|--years)
+                j=$(( $i + 1 ))
+                KEEP_YEARS="${REMAINING_OPTS[$j]}"
+                i=$(( $i + 2 ))
+                ;;
+            --dir)
+                j=$(( $i + 1 ))
+                BACKUP_ROOTDIR="${REMAINING_OPTS[$j]}"
+                i=$(( $i + 2 ))
+                ;;
+            *)  echo -e "Internal error - option '${RED}${arg}${NORMAL} was wrong!"
+                exit 1
+                ;;
+        esac
+    done
+
+    if [[ "${#REMAINING_ARGS[@]}" != "0" ]] ; then
+        error "Invalid arguments given."
+        echo >&2
+        usage >&2
+        exit 1
+    fi
+
+    local int_val=
+    local wrong_keep_vals="n"
+
+    int_val=$(( $KEEP_LAST + 0 ))
+    if [[ "${int_val}" -le "0" ]] ; then
+        error "Invalid number of keeping '${RED}${KEEP_LAST}${NORMAL}' backup sets."
+        wrong_keep_vals="y"
+    fi
+
+    int_val=$(( $KEEP_DAYS + 0 ))
+    if [[ "${int_val}" -le "0" ]] ; then
+        error "Invalid number of days '${RED}${KEEP_DAYS}${NORMAL}' to keep backup sets."
+        wrong_keep_vals="y"
+    fi
+
+    int_val=$(( $KEEP_WEEKS + 0 ))
+    if [[ "${int_val}" -le "0" ]] ; then
+        error "Invalid number of weeks '${RED}${KEEP_WEEKS}${NORMAL}' to keep backup sets."
+        wrong_keep_vals="y"
+    fi
+
+    int_val=$(( $KEEP_MONTHS + 0 ))
+    if [[ "${int_val}" -le "0" ]] ; then
+        error "Invalid number of months '${RED}${KEEP_MONTHS}${NORMAL}' to keep backup sets."
+        wrong_keep_vals="y"
+    fi
+
+    int_val=$(( $KEEP_YEARS + 0 ))
+    if [[ "${int_val}" -le "0" ]] ; then
+        error "Invalid number of years '${RED}${KEEP_YEARS}${NORMAL}' to keep backup sets."
+        wrong_keep_vals="y"
+    fi
+
+    if [[ "${wrong_keep_vals}" == "y" ]] ; then
+        echo >&2
+        description >&2
+        echo
+        usage >&2
+        exit 1
+    fi
+
+    if command -pv pdnsutil >/dev/null ; then
+        :
+    else
+        error "Command '${RED}pdnsutil${NORMAL}' not found."
+        echo >&2
+        exit 3
+    fi
+
+    if command -pv idn >/dev/null ; then
+        :
+    else
+        error "Command '${RED}idn${NORMAL}' not found."
+        echo >&2
+        exit 3
+    fi
+
+    if command -pv get-file-to-remove >/dev/null ; then
+        :
+    else
+        error "Command '${RED}get-file-to-remove${NORMAL}' not found."
+        echo >&2
+        exit 3
+    fi
+
+    BACKUP_DIR="${BACKUP_ROOTDIR}/${BACKUP_TIMESTAMP}"
+
+}
+
+#########################################
+# Some often used funktions
+
+#------------------------------------------------------------------------------
+MKDIR() {
+    local cmd="mkdir"
+    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
+#        return 0
+#    fi
+    echo
+}
+
+#------------------------------------------------------------------------------
+check_for_root() {
+    local my_id=$( id -u )
+    if [[ "${my_id}" != "0" ]] ; then
+        error "You must be ${RED}root${NORMAL} to execute this script."
+        echo >&2
+        exit 1
+    fi
+}
+
+################################################################################
+
+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 ..."
+
+    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() {
+
+    empty_line
+    info "Cleaning up old backup files and directories ..."
+
+    local verbose_option=""
+    if [[ "${VERBOSE}" == "y" ]] ; then
+        verbose_option="--verbose"
+    fi
+
+    local pattern="\"${BACKUP_ROOTDIR}\"/*"
+    local has_dirs='n'
+    local bdir=
+    local oifs="${IFS}"
+    IFS="
+"
+
+    local -a dirs=()
+
+    local cmd="get-file-to-remove --last ${KEEP_LAST}"
+    if [[ "${VERBOSE}" == "y" ]] ; then
+        cmd+=" --verbose"
+    else
+        cmd+=" --quiet"
+    fi
+    cmd+=" --days ${KEEP_DAYS}"
+    cmd+=" --weeks ${KEEP_WEEKS}"
+    cmd+=" --months ${KEEP_MONTHS}"
+    cmd+=" --years ${KEEP_YEARS}"
+    cmd+=" ${pattern}"
+
+    debug "Executing: ${cmd}"
+
+    for bdir in $( eval ${cmd} ) ; do
+        dirs+=( "${bdir}" )
+    done
+
+    IFS="${oifs}"
+
+    if [[ "${#dirs[*]}" -gt "0" ]] ; then
+      for bdir in "${dirs[@]}" ; do
+          info "Removing directory '${CYAN}${bdir}${NORMAL}' ..."
+          RM -r "${bdir}"
+      done
+    else
+      debug "No old backup directories to remove."
+    fi
+
+}
+
+#------------------------------------------------------------------------------
+get_zones() {
+
+    local zone=
+    for zone in $( pdnsutil list-all-zones | sort ) ; do
+        ZONES+=( "${zone}" )
+    done
+
+    if [[ "${VERBOSE}" == "y" ]] ; then
+        echo
+        echo "Zones to backup:"
+        for zone in "${ZONES[@]}" ; do
+            echo " * '${zone}'"
+        done
+        echo
+    fi
+
+
+}
+
+#------------------------------------------------------------------------------
+backup_zones() {
+
+    local db=
+    for zone in "${ZONES[@]}" ; do
+        backup_zone "${zone}"
+    done
+
+    empty_line
+    info "Backed up ${#ZONES[*]} zones."
+    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_zone() {
+
+    local zone="$1"
+    local zone_utf8=$( idn --idna-to-unicode "${zone}" )
+
+    local zone_show="'${zone}'"
+    if [[ "${zone}" != "${zone_utf8}" ]] ; then
+        zone_show="'${zone}' (${zone_utf8})"
+    fi
+
+    local verbose_option=
+    if [[ "${VERBOSE}" == "y" ]] ; then
+        verbose_option="--verbose"
+    fi
+
+    empty_line
+    info "Backing up zone '${GREEN}${zone}${NORMAL}' ..."
+
+    local output_txt="${zone_utf8}.txt"
+    local output_txt_compressed="${output_txt}.bz2"
+    local out_txt_tgt="${BACKUP_DIR}/${output_txt}"
+    local out_txt_tgt_compressed="${BACKUP_DIR}/${output_txt_compressed}"
+
+    local title=
+    local title=$( printf "All information about zone %s:" ${zone_show} )
+    local nr_chars=$( printf "${title}" | wc -c )
+    local title_line=$( printf '%*s\n' "${nr_chars}" '' | tr ' ' '-' )
+    echo -e "\n${title}\n${title_line}\n" > "${out_txt_tgt}"
+    pdnsutil show-zone "${zone}" >> "${out_txt_tgt}" 2>/dev/null
+
+    title="All Resource Records:" 
+    nr_chars=$( printf "${title}" | wc -c )
+    title_line=$( printf '%*s\n' "${nr_chars}" '' | tr ' ' '-' )
+    echo -e "\n${title}\n${title_line}\n" >> "${out_txt_tgt}"
+    pdnsutil list-zone "${zone}" >> "${out_txt_tgt}" 2>/dev/null
+
+    local blocks=$(stat -c "%b" "${out_txt_tgt}")
+    local bs=$(stat -c "%B" "${out_txt_tgt}")
+    local bytes=$(stat -c "%s" "${out_txt_tgt}")
+    local b_bytes=$(( ${blocks} * ${bs} ))
+    local k_bytes=$(( ${b_bytes} / 1024 ))
+    local msg=$( printf "Original size of %-60s    %10d Bytes => %7d KiB" \
+                        "'${output_txt}':" "${bytes}" "${k_bytes}" )
+    info "${msg}"
+
+    debug "Compressing '${out_txt_tgt}' ..."
+    bzip2 ${verbose_option} --best "${out_txt_tgt}"
+
+    blocks=$(stat -c "%b" "${out_txt_tgt_compressed}")
+    bs=$(stat -c "%B" "${out_txt_tgt_compressed}")
+    bytes=$(stat -c "%s" "${out_txt_tgt_compressed}")
+    b_bytes=$(( ${blocks} * ${bs} ))
+    k_bytes=$(( ${b_bytes} / 1024 ))
+
+    BYTES_TOTAL=$(( ${BYTES_TOTAL} + ${b_bytes} ))
+
+    local msg=$( printf "Compressed size of %-60s  %10d Bytes => %7d KiB" \
+                        "'${output_txt}':" "${bytes}" "${k_bytes}" )
+    info "${msg}"
+
+}
+
+
+
+################################################################################
+##
+## Main
+##
+################################################################################
+
+#------------------------------------------------------------------------------
+main() {
+
+    get_options "$@"
+    check_for_root
+
+    set_locale 'en_US.utf8'
+
+    prepare_dirs
+    info "Starting backup ..."
+    cleanup_old_backups
+    get_zones
+    backup_zones
+
+    empty_line
+    info "Finished."
+
+}
+
+main "$@"
+
+exit 0
+
+# vim: ts=4 et list