#!/usr/bin/env bash # PgBackrest backup wrapper script ## FUNCTIONS # METHOD: convert_time # PARAMS: timestamp in seconds or with milliseconds (nnnn.nnnn) # RETURN: formated string with human readable time (d/h/m/s) # CALL : var=$(convert_time $timestamp); # DESC : converts a timestamp or a timestamp with float milliseconds # to a human readable format # output is in days/hours/minutes/seconds function convert_time { timestamp=${1}; # round to four digits for ms timestamp=$(printf "%1.4f" "$timestamp"); # get the ms part and remove any leading 0 ms=$(echo "${timestamp}" | cut -d "." -f 2 | sed -e 's/^0*//'); timestamp=$(echo "${timestamp}" | cut -d "." -f 1); timegroups=(86400 3600 60 1); # day, hour, min, sec timenames=("d" "h" "m" "s"); # day, hour, min, sec output=( ); time_string=""; for timeslice in "${timegroups[@]}"; do # floor for the division, push to output output[${#output[*]}]=$(awk "BEGIN {printf \"%d\", ${timestamp}/${timeslice}}"); timestamp=$(awk "BEGIN {printf \"%d\", ${timestamp}%${timeslice}}"); done; for ((i=0; i<${#output[@]}; i++)); do if [ "${output[$i]}" -gt 0 ] || [ -n "$time_string" ]; then if [ -n "${time_string}" ]; then time_string=${time_string}" "; fi; time_string=${time_string}${output[$i]}${timenames[$i]}; fi; done; if [ -n "${ms}" ] && [ "${ms}" != "nan" ] && [ "${ms}" -gt 0 ]; then time_string=${time_string}" ${ms}ms"; fi; # just in case the time is 0 if [ -z "${time_string}" ]; then time_string="0s"; fi; echo -n "${time_string}"; } ## VARIABLES REGEX_COMMENT="^[\ \t]*#"; INI_BLOCK="^\[[A-Za-z]*\]"; BASE_FOLDER=$(dirname "$(readlink -f "$0")")"/"; CONFIG="${BASE_FOLDER}../config/"; BACKUP_TYPE=""; OVERRIDE_STANZA=""; LIST_STANZA=0; TEST=0; config_file="${CONFIG}pgbackrest.cfg"; stanza_file="${CONFIG}stanza.cfg"; if [ -f "${config_file}" ]; then # shellcheck disable=SC1090 # shellcheck disable=SC2283 source <(grep = "${config_file}" | sed 's/ *= */=/g') else echo "Missing config file ${config_file}"; exit; fi; if [ -z "${sudo_user}" ]; then echo "sudo_user not set in the config file ${config_file}"; exit; fi; if ! id "${sudo_user}" &>/dev/null; then echo "Sudo user ${sudo_user} does not exist, skipping stanza"; exit fi; if [ -z "${pgbackrest_config}" ] ||[ ! -f "${pgbackrest_config}" ]; then echo "Missing pgbackrest default config file ${pgbackrest_config}"; exit; fi; # stanza list file if [ ! -f "${stanza_file}" ]; then echo "Missing stanza config file ${stanza_file}"; exit; fi; PRINTF_BLOCK="=== [%-8s: %19s] ==[%s]===============>\n"; ## OPT PARSEIN while getopts ":b:s:lt" opt; do case "${opt}" in b) # backuptype BACKUP_TYPE="${OPTARG}"; ;; s) # stanza OVERRIDE_STANZA="${OPTARG}"; ;; l) # list LIST_STANZA=1; ;; t) # test TEST=1; ;; :) echo "Option -$OPTARG requires an argument." ;; \?) echo -e "\n Option does not exist: ${OPTARG}\n"; echo "-b (backuptype): full/diff"; echo "-s (stanza): override stanza name, must be in list" echo "-l list available stanza" echo "-t test run" exit 1; ;; esac; done; ## check if OVERIDE_STANZA has a repo target flag set, get the name only for checks OVERRIDE_STANZA_NAME=""; if [ -n "${OVERRIDE_STANZA}" ]; then OVERRIDE_STANZA_NAME=$(echo "${OVERRIDE_STANZA}" | cut -d ":" -f 1); fi; ## SCRIPT if [ $TEST -eq 1 ]; then echo "[..........] TEST RUN ONLY"; fi; if [ $LIST_STANZA -eq 1 ]; then echo "+ Stanza List:"; while read -r stanza; do # skip empty [ -z "${stanza}" ] && continue; # skip starting with # [[ "${stanza}" =~ ${REGEX_COMMENT} ]] && continue; # skip the ini header blocks [[ "${stanza}" =~ ${INI_BLOCK} ]] && continue; # split the stanz int stanza and stanza repo list stanza_name=$(echo "${stanza}" | cut -d ":" -f 1); stanza_repo=$(echo "${stanza}" | awk -F':' '{print (NF>1) ? $2 : ""}'); # if override stanza matching if [ "${stanza_name}" = "${OVERRIDE_STANZA_NAME}" ]; then OVERRIDE_STANZA_REPO=$(echo "${OVERRIDE_STANZA}" | awk -F':' '{print (NF>1) ? $2 : ""}'); if [ -n "${OVERRIDE_STANZA_REPO}" ] && [ -n "${stanza_repo}" ] && [ "${stanza_repo}" = "${OVERRIDE_STANZA_REPO}" ]; then echo "[*] ${stanza}"; elif [ -z "${OVERRIDE_STANZA_REPO}" ]; then echo "[*] ${stanza}"; else echo "[ ] ${stanza}"; fi; else echo "[ ] ${stanza}"; fi; done <<< "$(cat "${stanza_file}")"; echo ""; echo "Entry marked with '*' is set as override stanza, and will be the only ones backed up"; echo ""; exit; fi; if [ -z "${BACKUP_TYPE}" ]; then echo "[!] Backup type must be set as -b full or -b diff"; exit; fi; if [ "${BACKUP_TYPE}" != "full" ] && [ "${BACKUP_TYPE}" != "diff" ]; then echo "[!] Backup type can only be 'full' or 'diff'"; exit; fi; echo "* PgBackrest Backup run type '${BACKUP_TYPE}' on $(date +'%F')"; # run backup while read -r stanza; do # skip empty [ -z "${stanza}" ] && continue; # skip starting with # [[ "${stanza}" =~ ${REGEX_COMMENT} ]] && continue; # skip the ini header blocks [[ "${stanza}" =~ ${INI_BLOCK} ]] && continue; # split into the name and repo list stanza_name=$(echo "${stanza}" | cut -d ":" -f 1); stanza_repo=$(echo "${stanza}" | awk -F':' '{print (NF>1) ? $2 : ""}'); # if we have a config stanza_config=$(echo "${stanza}" | awk -F':' '{print (NF>1) ? $3 : ""}'); # for possible override super user stanza_super_user=$(echo "${stanza}" | awk -F':' '{print (NF>3) ? $4 : ""}'); # override stanza check if [ -n "${OVERRIDE_STANZA}" ]; then # skip if not matching name if [ "${stanza_name}" != "${OVERRIDE_STANZA_NAME}" ]; then continue fi; # if we have repo set, check if repo is matching OVERRIDE_STANZA_REPO=$(echo "${OVERRIDE_STANZA}" | awk -F':' '{print (NF>1) ? $2 : ""}'); if [ -n "${OVERRIDE_STANZA_REPO}" ] && [ "${stanza_repo}" != "${OVERRIDE_STANZA_REPO}" ]; then continue fi; # set repo from override stanza_repo="${OVERRIDE_STANZA_REPO}"; # config OVERRIDE_STANZA_CONFIG=$(echo "${OVERRIDE_STANZA}" | awk -F':' '{print (NF>2) ? $3 : ""}'); # if we have a config set in the stanza config, but none in the override, use the one from the stanza config if [ -z "${OVERRIDE_STANZA_CONFIG}" ] && [ -n "${stanza_config}" ]; then OVERRIDE_STANZA_CONFIG="${stanza_config}"; else stanza_config="${OVERRIDE_STANZA_CONFIG}"; fi; # super user OVERRIDE_STANZA_SUPER_USER=$(echo "${OVERRIDE_STANZA}" | awk -F':' '{print (NF>3) ? $4 : ""}'); if [ -z "${OVERRIDE_STANZA_SUPER_USER}" ] && [ -n "${stanza_super_user}" ]; then OVERRIDE_STANZA_SUPER_USER="${stanza_super_user}"; else stanza_super_user="${OVERRIDE_STANZA_SUPER_USER}"; fi; fi; # set override sudo user if [ -n "${stanza_super_user}" ]; then if ! id "${stanza_super_user}" &>/dev/null; then echo "[!] Sudo user ${stanza_super_user} for stanza ${stanza_name} does not exist, skipping stanza"; continue; fi; sudo_user="${stanza_super_user}"; fi; # build the call command CALL=( "sudo" "-u" "${sudo_user}" "pgbackrest" "--type=${BACKUP_TYPE}" "--stanza=${stanza_name}" ); _stanza_config="${pgbackrest_config}"; if [ -f "${stanza_config}" ]; then _stanza_config="${stanza_config}"; fi; stanza_check=$(grep -E "^\[${stanza_name}\]$" "${_stanza_config}"); if [ -z "${stanza_check}" ]; then echo "[!] Stanza ${stanza_name} not found in config file ${_stanza_config}, skipping stanza"; continue; fi; if [ -n "${stanza_repo}" ]; then # check if repo exists in config file with "repo-" repo_check=$(grep -E "^repo${stanza_repo}-" "${_stanza_config}"); if [ -z "${repo_check}" ]; then echo "[!] Stanza repo ${stanza_repo} for stanza ${stanza_name} not found in config file ${_stanza_config}, skipping stanza"; continue; fi; CALL=("${CALL[@]}" "--repo=${stanza_repo}"); fi; if [ -n "${stanza_config}" ]; then if [ ! -f "${stanza_config}" ]; then echo "[!] Stanza config file ${stanza_config} for stanza ${stanza_name} not found, skipping stanza"; continue; fi; CALL=("${CALL[@]}" "--config=${stanza_config}"); fi; CALL=("${CALL[@]}" "backup"); # main backup start START=$(date +'%s'); # shellcheck disable=SC2059 printf "${PRINTF_BLOCK}" "START" "$(date +'%F %T')" "${stanza}"; # --log-level-console=info if [ $TEST -eq 1 ]; then echo "${CALL[@]}"; else # sudo -u "${sudo_user}" pgbackrest --type="${BACKUP_TYPE}" --stanza="${stanza}" ${option_repo} backup; "${CALL[@]}"; fi; DURATION=$(( $(date +'%s') - START )); # shellcheck disable=SC2059 printf "${PRINTF_BLOCK}" "END" "$(convert_time ${DURATION})" "${stanza}"; done <<< "$(cat "${stanza_file}")"; echo "* PgBackrest Backup end"; if [ $TEST -eq 1 ]; then echo "[..........] TEST RUN ONLY"; fi; ## __END__