#!/usr/bin/env bash ### FTP config FTPHOST="your.ftp.com" FTPPATH="dump/" FTPUSER="" FTPPASS="" ### Purge after x days PURGE=3 ### Storage ID to move STORAGE="local-backup" ##### CONFIG END ##### ### FUNCTIONS function log { echo "[HOOK] $*" } function purge_ftp { VM=$1 log "PURGE for VM ${VM} started." PURGEDATE=$(date --date="${PURGE} days ago" +%s) FILES=$(curl -s -u "${FTPUSER}:${FTPPASS}" --list-only "ftp://${FTPHOST}/${FTPPATH}" | sort) RETURNCODE=$? if [[ $RETURNCODE -ne 0 ]]; then log " LIST: ftp://${FTPHOST}/${FTPPATH} - FAILED (curl): $RETURNCODE" return $RETURNCODE fi for FILE in ${FILES[@]}; do SPLITFILE="${FILE/./ }"; SPLITFILE="${SPLITFILE//-/ }"; SPLITFILE=(${SPLITFILE//_/-}) VMSTR="${SPLITFILE[2]}" DATESTR="${SPLITFILE[3]}" TIMESTR="${SPLITFILE[4]}"; TIMESTR="${TIMESTR//-/:}" if [[ $(date --date="$DATESTR $TIMESTR" +%s) -lt $PURGEDATE && "$VMSTR" == "$VM" ]]; then curl -s -u "${FTPUSER}:${FTPPASS}" --head -Q "-DELE ${FTPPATH}${FILE}" "ftp://${FTPHOST}" RETURNCODE=$? if [[ $RETURNCODE -ne 0 ]]; then log " DELETE: $FILE - FAILED (curl): $RETURNCODE" return $RETURNCODE else log " DELETE: $FILE - SUCCESS" fi elif [[ "$VMSTR" == "$VM" ]]; then log " KEEP: $FILE - not older than ${PURGE} days" fi done return 0 } function upload_ftp { FILE=$1 log "UPLOAD for ${FILE} started." RETURNCODE=1 TRIES=0 while [[ $RETURNCODE -ne 0 && $TRIES -lt 3 ]]; do ((TRIES++)) curl -s -u "${FTPUSER}:${FTPPASS}" --keepalive-time 30 -T "$FILE" "ftp://${FTPHOST}/${FTPPATH}" RETURNCODE=$? if [[ $RETURNCODE -ne 0 ]]; then # try to mitigate curl (28): Timeout, curl(55) # usually file transfers fine, but control channel is killed by the firewall FILENAME=$(basename "${FILE}") LOCALSIZE=$(stat --printf="%s" "${FILE}" | tr -d '[[:space:]]') REMOTESIZE=$(curl -sI -u "${FTPUSER}:${FTPPASS}" "ftp://${FTPHOST}/${FTPPATH}${FILENAME}" | awk '/Content-Length/ { print $2 }' | tr -d '[[:space:]]') log " UPLOAD #${TRIES}: $FILE to ftp://${FTPHOST}/${FTPPATH} - local: ${LOCALSIZE}, remote: ${REMOTESIZE}" if [[ "$REMOTESIZE" -eq "$LOCALSIZE" ]]; then log " UPLOAD #${TRIES}: $FILE to ftp://${FTPHOST}/${FTPPATH} - WARN (curl): $RETURNCODE, but seems complete" RETURNCODE=0 else log " UPLOAD #${TRIES}: $FILE to ftp://${FTPHOST}/${FTPPATH} - FAILED (curl): $RETURNCODE" if [[ $RETURNCODE -eq 55 ]]; then curl -s -u "${FTPUSER}:${FTPPASS}" --head -Q "-DELE ${FTPPATH}${FILENAME}" "ftp://${FTPHOST}" fi fi fi done if [[ $RETURNCODE -ne 0 ]]; then log " UPLOAD: $FILE to ftp://${FTPHOST}/${FTPPATH} - FAILED PERMANENTLY" else log " UPLOAD: $FILE to ftp://${FTPHOST}/${FTPPATH} - SUCCESS" fi return $RETURNCODE } ### MAIN PHASE=$1 if [[ "$PHASE" == "job-start" || "$PHASE" == "job-end" || "$PHASE" == "job-abort" ]]; then #DUMPDIR #STOREID exit 0 fi if [[ "$PHASE" == "backup-start" || "$PHASE" == "backup-end" || "$PHASE" == "backup-abort" || "$PHASE" == "log-end" || "$PHASE" == "pre-stop" || "$PHASE" == "pre-restart" || "$PHASE" == "post-restart" ]]; then MODE=$2 # stop,suspend,snapshot VMID=$3 #DUMPDIR #STOREID #VMTYPE # openvz,qemu #HOSTNAME if [[ "$PHASE" == "backup-end" && "$STOREID" == "$STORAGE" ]]; then #TARFILE log "transfer backup archive of VM ${VMID} to FTP" upload_ftp "$TARFILE" RETURNCODE=$? if [[ $RETURNCODE -ne 0 ]]; then exit $RETURNCODE; fi log "remove local backup archive of VM ${VMID}" rm "$TARFILE" log "purge old backup and log files of VM ${VMID} from FTP" purge_ftp $VMID RETURNCODE=$? exit $RETURNCODE fi if [[ "$PHASE" == "log-end" && "$STOREID" == "$STORAGE" ]]; then #LOGFILE log "transfer log file of VM ${VMID} to FTP" upload_ftp "$LOGFILE" RETURNCODE=$? if [[ $RETURNCODE -ne 0 ]]; then exit $RETURNCODE; fi log "remove local log file of VM ${VMID}" rm "$LOGFILE" exit $RETURNCODE fi exit 0 fi # manual tasks if [[ $PHASE == "manual-cleanup" ]]; then VMID=$2 if [[ -z $VMID ]]; then echo "Usage: $(basename $0) manual-cleanup " exit 1 fi log "purge old backup and log files of VM ${VMID} from FTP" purge_ftp $VMID RETURNCODE=$? log "purge old backup and log files of VM ${VMID} from local storage" rm -r ${STORAGEDIR}/dump/vzdump-*-${VMID}-* exit $RETURNCODE fi