diff --git a/lxc-backup.sh b/lxc-backup.sh index bb66228..05f25c1 100755 --- a/lxc-backup.sh +++ b/lxc-backup.sh @@ -1,64 +1,52 @@ #!/usr/bin/env bash main() { + # Set default options + local cmd="${CMD:-lxc}" + local backup_dir="${BACKUP_DIR:-$PWD}" + local compression="${COMPRESSION:-zstd}" + local vm_list + local retention=0 + while getopts ":l:d:t:x:c:h" opt; do case "${opt}" in l) - unset VM_FILTER - IFS="," read -r -a VM_FILTER <<< "$OPTARG" - ;; + IFS="," read -r -a vm_list <<< "$OPTARG";; d) - local backup_location="${OPTARG}" - backup_location="${backup_location%/}" - ;; + backup_dir="${OPTARG%/}";; t) - declare -g TARGET="$OPTARG" - ;; + cmd="$OPTARG";; x) - declare -g EXTENSION - if [[ "${OPTARG,,}" == 'none' ]]; then - EXTENSION="" - else - command -v "$OPTARG" >/dev/null 2>&1 || { echo "$OPTARG command not found"; exit 1; } - EXTENSION="${OPTARG,,}" - fi - - declare -g COMPRESSION="$OPTARG" - ;; + compression="$OPTARG";; c) - declare -g run_cleanup=y - declare -g files_to_keep="${OPTARG}" - ;; - h) - usage - ;; - *) - usage - ;; + retention="${OPTARG}";; + h|*) + usage;; esac done - backup_location=${backup_location:-$PWD} + # Helper functions + lxc() { command "$cmd" "$@"; } + list_vms() { lxc list -c n -f csv; } - if [[ "${#VM_FILTER[*]}" -gt 0 ]]; then - backup_vms "$backup_location" "${VM_FILTER[@]}" - else - backup_vms "$backup_location" + if ((${#vm_list[@]} == 0)); then + mapfile -t vm_list < <(list_vms) fi -} -lxc() { - local lxc_cmd="${TARGET:-lxc}" - command "$lxc_cmd" "$@" -} + for vm in "${vm_list[@]}"; do + start_backup "$vm" "$backup_dir" "$compression" -list_vms() { - lxc list -c n -f csv + if (( retention > 0 )); then + prune_backups "$vm" "$backup_dir" "$retention" + fi + done + + exit 0 } usage() { cat <] [-c ] [-h] +Usage: ${0##*/} [-l vm[,vm...]] [-d ] [-t lxc|incus] [-x ] [-c ] [-h] Options: -l Comma-separated list of VMs to back up @@ -72,47 +60,39 @@ EOF exit 1 } -backup_vms() { - local backup_location="$1"; shift - local vms=("$@") - - # when no argument use list of all VMs - if ((${#vms[@]} == 0)); then - mapfile -t vms < <(list_vms) - fi - - for vm in "${vms[@]}"; do - [[ -n $vm ]] || continue - start_backup "$vm" "$backup_location" - done -} - start_backup() { local vm=$1 - local backup_base="$2" - local compression="${COMPRESSION:-none}" - local ext="${EXTENSION}" ; [[ -n "$ext" ]] && ext=".${ext}" - local destination="${backup_base}/${vm}" + local backup_dir="$2" + local compression="$3" + local ext=".${compression}" + + if [[ "$compression" == 'none' ]]; then + ext="" + else + command -v "$compression" >/dev/null 2>&1 || { echo "$compression command not found"; exit 1; } + fi + + local vm_dir="${backup_dir}/${vm}" local backup_name backup_name="${vm}_$(date +"%Y-%m-%d_%H-%M-%S").tar${ext}" - [[ -d "$backup_base" ]] || { echo "${backup_base} is not a directory"; exit 1; } - [[ -d "$destination" ]] || mkdir "$destination" - [[ -f "${destination}/${backup_name}" ]] && { echo "Backup already exists"; return 2; } - - lxc export "$vm" --instance-only --compression "${compression}" "${destination}/${backup_name}" || { echo "Error exporting $vm"; return 1; } - - [[ -n "$run_cleanup" ]] && prune_backups "$destination" + [[ -d "$backup_dir" ]] || { echo "${backup_dir} is not a directory"; exit 1; } + [[ -d "$vm_dir" ]] || mkdir "$vm_dir" || { echo "Cannont create $vm_dir"; exit 1; } + [[ -f "${vm_dir}/${backup_name}" ]] && { echo "Backup already exists"; return 2; } + echo "starting $vm" + lxc export "$vm" --instance-only --compression "${compression}" "${vm_dir}/${backup_name}" || { echo "Error exporting $vm"; return 1; } } prune_backups() { - local backup_directory="$1" - mapfile -t backup_files < <(find "$backup_directory" -maxdepth 1 -type f -printf '%T@ %p\n' | sort -n | head -n "-${files_to_keep}" | cut -d' ' -f2-) + local vm="$1" + local backup_dir="$2" + local retention="$3" + mapfile -t backup_files < <(find "${backup_dir}/${vm}" -maxdepth 1 -type f -printf '%T@ %p\n' | sort -n | head -n "-${retention}" | cut -d' ' -f2-) if [[ ${#backup_files[@]} -gt 0 ]]; then - echo "Removing ${#backup_files[@]} old backup files for $vm (keeping last $files_to_keep files)" - printf '%s\0' "${backup_files[@]}" | xargs -0 rm -fv + echo "Removing ${#backup_files[@]} old backup files for $vm (keeping last $retention files)" + printf '%s\0' "${backup_files[@]}" | xargs -0 rm -fv > /dev/null else echo "No old backups to remove for $vm" fi