Files
programming/bash/corellia_backup_VM.sh
T
2026-05-23 09:09:39 +02:00

216 lines
4.8 KiB
Bash

#!/usr/bin/env bash
set -u # no unset vars
########################
# Configuration
########################
SECONDS_WAIT=60
DESTINATION_DIR="/mnt/unprotected/tmp/VM/backup/corellia"
VMHOME="/vm/ssd0/" # base dir for VM data; adapt as needed
DAYS_TO_KEEP_VM_BACKUP=60
MAIL_TO="vplesnila@gmail.com"
MAIL_FROM="plesnila.valeriu@orange.fr"
MAIL_SUBJECT_OK="VM backup success"
MAIL_SUBJECT_FAIL="VM backup failure"
LOG_DIR="/root/tmp"
LOGFILE="${LOG_DIR}/vm_backup_$(date +%F_%H%M%S).log"
########################
# Logging setup
########################
mkdir -p "$LOG_DIR" "$DESTINATION_DIR"
# Log everything to file and stdout
exec > >(tee -a "$LOGFILE") 2>&1
echo "===== VM backup started at $(date) ====="
FAILED=0
fail() {
echo "ERROR: $*" >&2
FAILED=1
}
send_mail() {
local subject="$1"
{
echo "From: ${MAIL_FROM}"
echo "To: ${MAIL_TO}"
echo "Subject: ${subject}"
echo
cat "$LOGFILE"
} | msmtp "$MAIL_TO"
}
########################
# Helper functions
########################
list_all_vms() {
virsh list --all --name | sed '/^$/d'
}
list_running_vms() {
virsh list --name | sed '/^$/d'
}
wait_and_check_offline() {
local wait_seconds="$1"
echo "Waiting ${wait_seconds}s..."
sleep "$wait_seconds"
local still_running
still_running=$(list_running_vms || true)
if [[ -n "$still_running" ]]; then
echo "Still running VMs:"
echo "$still_running"
return 1
fi
echo "All VMs are offline."
return 0
}
backup_vm() {
local vm="$1"
local date_str
date_str="$(date +%F)"
local backup_file="${DESTINATION_DIR}/${vm}_${date_str}.tar.gz"
local xml_file="${DESTINATION_DIR}/${vm}_${date_str}.xml"
echo "----- Backing up VM: ${vm} -----"
# 1. Export XML definition
echo "Exporting XML definition to ${xml_file}"
if ! virsh dumpxml "$vm" > "$xml_file"; then
fail "Failed to dump XML for VM '$vm'"
return 1
fi
# 2. Backup VM storage
if [[ ! -d "$VMHOME" ]]; then
fail "VMHOME directory '$VMHOME' does not exist for VM '$vm'"
return 1
fi
cd "$VMHOME" || { fail "Cannot cd to $VMHOME for VM '$vm'"; return 1; }
echo "Creating compressed archive: $backup_file"
if ! tar -cvf - ${vm} | pigz > "$backup_file"; then
fail "Backup failed for VM '$vm'"
return 1
fi
# 3. Cleanup old backups
echo "Cleaning old backups for VM '$vm' older than ${DAYS_TO_KEEP_VM_BACKUP} days"
if ! find "$DESTINATION_DIR" -maxdepth 1 -type f \
\( -name "${vm}_*.tar.gz" -o -name "${vm}_*.xml" \) \
-mtime +"$DAYS_TO_KEEP_VM_BACKUP" -print -delete; then
fail "Failed to clean old backups for VM '$vm'"
return 1
fi
echo "Backup completed for VM '$vm'"
return 0
}
start_all_vms() {
echo "===== Starting ALL defined VMs ====="
local all_vms
all_vms=$(virsh list --all --name | sed '/^$/d')
if [[ -z "$all_vms" ]]; then
echo "No VMs defined on this host; nothing to start."
return 0
fi
while read -r vm; do
[[ -z "$vm" ]] && continue
echo "Starting VM: $vm"
if ! virsh start "$vm"; then
fail "Failed to start VM '$vm'"
fi
done <<< "$all_vms"
echo "===== VM startup phase completed ====="
}
########################
# Main logic
########################
ALL_VMS=$(list_all_vms)
if [[ -z "$ALL_VMS" ]]; then
echo "No VMs defined on this host."
fi
echo "All VMs:"
echo "$ALL_VMS"
RUNNING_VMS=$(list_running_vms)
echo "Running VMs:"
echo "${RUNNING_VMS:-<none>}"
# Graceful shutdown
if [[ -n "$RUNNING_VMS" ]]; then
echo "Shutting down VMs gracefully..."
while read -r vm; do
[[ -z "$vm" ]] && continue
echo "virsh shutdown $vm"
if ! virsh shutdown "$vm"; then
fail "Graceful shutdown failed for VM '$vm'"
fi
done <<< "$RUNNING_VMS"
else
echo "No running VMs to shut down gracefully."
fi
# Wait and check
if ! wait_and_check_offline "$SECONDS_WAIT"; then
echo "Some VMs still running after graceful shutdown; forcing shutdown..."
STILL_RUNNING=$(list_running_vms)
while read -r vm; do
[[ -z "$vm" ]] && continue
echo "virsh destroy $vm"
if ! virsh destroy "$vm"; then
fail "Force shutdown failed for VM '$vm'"
fi
done <<< "$STILL_RUNNING"
fi
# If any failure so far, send failure mail and exit
if [[ "$FAILED" -ne 0 ]]; then
echo "Failures detected before backup phase."
send_mail "$MAIL_SUBJECT_FAIL"
echo "===== VM backup finished with FAILURE at $(date) ====="
exit 1
fi
# Backup each VM
for vm in $ALL_VMS; do
[[ -z "$vm" ]] && continue
backup_vm "$vm" || FAILED=1
done
echo "Starting all VMs..."
start_all_vms
# Final status and mail
if [[ "$FAILED" -ne 0 ]]; then
echo "One or more VM backups failed."
send_mail "$MAIL_SUBJECT_FAIL"
echo "===== VM backup finished with FAILURE at $(date) ====="
exit 1
else
echo "All VM backups completed successfully."
send_mail "$MAIL_SUBJECT_OK"
echo "===== VM backup finished with SUCCESS at $(date) ====="
exit 0
fi