216 lines
4.8 KiB
Bash
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
|
||
|
|
|