ProxMenux/scripts/ProxmoxBakupRestore.sh
2025-01-10 18:13:52 +01:00

479 lines
17 KiB
Bash

#!/bin/bash
# Configuración
SCRIPT_VERSION="1.3"
LOG_FILE="/var/log/proxmox_backup_restore.log"
# Colores para salida
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m' # No Color
# Variable global para el modo de interfaz
INTERFACE_MODE=""
# Funciones de utilidad
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
echo -e "$1"
}
error() {
log "${RED}ERROR: $1${NC}"
if [ "$INTERFACE_MODE" = "whiptail" ]; then
whiptail --title "Error" --msgbox "$1" 8 78
else
echo -e "${RED}ERROR: $1${NC}"
fi
exit 1
}
success() {
log "${GREEN}$1${NC}"
if [ "$INTERFACE_MODE" = "whiptail" ]; then
whiptail --title "Éxito" --msgbox "$1" 8 78
else
echo -e "${GREEN}$1${NC}"
fi
}
warning() {
log "${YELLOW}ADVERTENCIA: $1${NC}"
if [ "$INTERFACE_MODE" = "whiptail" ]; then
whiptail --title "Advertencia" --msgbox "$1" 8 78
else
echo -e "${YELLOW}ADVERTENCIA: $1${NC}"
fi
}
# Función para seleccionar el modo de interfaz
select_interface_mode() {
if [ "$(tty)" = "/dev/tty1" ] || [ "$(tty)" = "/dev/console" ]; then
echo "Ejecutando en consola física. Usando interfaz de texto."
INTERFACE_MODE="echo"
elif command -v whiptail >/dev/null 2>&1; then
echo "Ejecutando en terminal remota. Usando interfaz whiptail."
INTERFACE_MODE="whiptail"
else
echo "whiptail no está disponible. Usando interfaz de texto."
INTERFACE_MODE="echo"
fi
}
# Función para mostrar el menú principal
show_main_menu() {
while true; do
if [ "$INTERFACE_MODE" = "whiptail" ]; then
CHOICE=$(whiptail --title "Proxmox Backup & Restore Tool" --menu "Seleccione una opción:" 15 60 3 \
"1" "Realizar copia de seguridad" \
"2" "Restaurar copia de seguridad" \
"3" "Salir" 3>&1 1>&2 2>&3)
else
echo "Proxmox Backup & Restore Tool"
echo "1. Realizar copia de seguridad"
echo "2. Restaurar copia de seguridad"
echo "3. Salir"
read -p "Seleccione una opción: " CHOICE
fi
case $CHOICE in
1) perform_backup ;;
2) perform_restore ;;
3) exit 0 ;;
*) echo "Opción no válida. Por favor, intente de nuevo." ;;
esac
done
}
# Función para seleccionar la ubicación de la copia de seguridad
select_backup_location() {
local options=()
local i=1
# Ubicaciones recomendadas
options+=("/root/backups/" "/var/lib/vz/dump/")
# Detectar discos USB
local usb_disks=$(lsblk -ndo NAME,TRAN,SIZE | awk '$2=="usb" {print "/dev/"$1}')
options+=($usb_disks)
# Otros discos disponibles
local other_disks=$(lsblk -ndo NAME,SIZE,FSTYPE | grep -vE "^(sd[a-z]|nvme[0-9]n[0-9])" | awk '$3!="" {print "/dev/"$1}')
options+=($other_disks)
if [ "$INTERFACE_MODE" = "whiptail" ]; then
BACKUP_LOCATION=$(whiptail --title "Seleccionar ubicación de copia de seguridad" \
--menu "Elija dónde guardar la copia de seguridad:" 20 78 10 \
$(for i in "${!options[@]}"; do echo "$i ${options[$i]}"; done) \
"C" "Introducir ruta personalizada" 3>&1 1>&2 2>&3)
else
echo "Seleccionar ubicación de copia de seguridad:"
for i in "${!options[@]}"; do
echo "$i. ${options[$i]}"
done
echo "C. Introducir ruta personalizada"
read -p "Elija dónde guardar la copia de seguridad: " BACKUP_LOCATION
fi
if [[ $BACKUP_LOCATION == "C" ]]; then
if [ "$INTERFACE_MODE" = "whiptail" ]; then
BACKUP_LOCATION=$(whiptail --inputbox "Introduzca la ruta personalizada:" 8 78 "/mnt/backup" --title "Ruta personalizada" 3>&1 1>&2 2>&3)
else
read -p "Introduzca la ruta personalizada: " BACKUP_LOCATION
fi
elif [[ $BACKUP_LOCATION =~ ^[0-9]+$ ]]; then
BACKUP_LOCATION=${options[$BACKUP_LOCATION]}
fi
echo "$BACKUP_LOCATION"
}
# Función para realizar la copia de seguridad
perform_backup() {
local BACKUP_LOCATION=$(select_backup_location)
if [ "$INTERFACE_MODE" = "whiptail" ]; then
local BACKUP_TYPE=$(whiptail --title "Tipo de copia de seguridad" --menu "Seleccione el tipo de copia de seguridad:" 15 60 2 \
"1" "Copia de seguridad total" \
"2" "Copia de seguridad personalizada" 3>&1 1>&2 2>&3)
else
echo "Tipo de copia de seguridad:"
echo "1. Copia de seguridad total"
echo "2. Copia de seguridad personalizada"
read -p "Seleccione el tipo de copia de seguridad: " BACKUP_TYPE
fi
local TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
local BACKUP_PATH="${BACKUP_LOCATION}/proxmox_backup_${TIMESTAMP}"
mkdir -p "$BACKUP_PATH" || error "No se pudo crear el directorio de copia de seguridad."
case $BACKUP_TYPE in
1) backup_total "$BACKUP_PATH" ;;
2) backup_personalizada "$BACKUP_PATH" ;;
*) error "Opción no válida." ;;
esac
# Guardar versión de Proxmox
pveversion > "$BACKUP_PATH/pve_version.txt"
# Guardar mapa de rutas de almacenamiento
pvesm status --output-format=json > "$BACKUP_PATH/storage_paths.json"
success "Copia de seguridad completada en $BACKUP_PATH"
}
# Función para realizar copia de seguridad total
backup_total() {
local BACKUP_PATH=$1
log "Iniciando copia de seguridad total..."
# Copiar configuraciones de Proxmox
cp -r /etc/pve "$BACKUP_PATH/etc_pve" || warning "Error al copiar /etc/pve"
cp -r /etc/network "$BACKUP_PATH/etc_network" || warning "Error al copiar /etc/network"
cp /etc/hostname "$BACKUP_PATH/etc_hostname" || warning "Error al copiar /etc/hostname"
cp /etc/hosts "$BACKUP_PATH/etc_hosts" || warning "Error al copiar /etc/hosts"
# Copiar logs relevantes
mkdir -p "$BACKUP_PATH/var_log"
cp /var/log/pveam.log "$BACKUP_PATH/var_log/" || warning "Error al copiar pveam.log"
cp /var/log/pvedaemon.log "$BACKUP_PATH/var_log/" || warning "Error al copiar pvedaemon.log"
# Copiar configuraciones de almacenamiento
cp /etc/pve/storage.cfg "$BACKUP_PATH/storage.cfg" || warning "Error al copiar storage.cfg"
# Copiar configuraciones de usuarios y permisos
cp /etc/pve/user.cfg "$BACKUP_PATH/user.cfg" || warning "Error al copiar user.cfg"
cp /etc/pve/groups.cfg "$BACKUP_PATH/groups.cfg" || warning "Error al copiar groups.cfg"
# Copiar configuraciones de firewall
cp -r /etc/pve/firewall "$BACKUP_PATH/firewall" || warning "Error al copiar configuraciones de firewall"
# Copiar metadatos de VMs y contenedores (sin incluir discos o snapshots)
mkdir -p "$BACKUP_PATH/vms_metadata"
for vmid in $(qm list | awk '{if(NR>1) print $1}'); do
qm config $vmid > "$BACKUP_PATH/vms_metadata/vm_${vmid}.conf"
done
mkdir -p "$BACKUP_PATH/cts_metadata"
for ctid in $(pct list | awk '{if(NR>1) print $1}'); do
pct config $ctid > "$BACKUP_PATH/cts_metadata/ct_${ctid}.conf"
done
log "Copia de seguridad total completada."
}
# Función para realizar copia de seguridad personalizada
backup_personalizada() {
local BACKUP_PATH=$1
log "Iniciando copia de seguridad personalizada..."
local options=(
"1" "Configuración del sistema Proxmox" ON
"2" "Configuraciones de almacenamiento" ON
"3" "Configuraciones de red" ON
"4" "Usuarios y permisos" ON
"5" "Logs del sistema relevantes" OFF
"6" "Configuraciones de firewall" ON
"7" "Metadatos de VMs y contenedores" ON
)
local SELECTED_OPTIONS
if [ "$INTERFACE_MODE" = "whiptail" ]; then
SELECTED_OPTIONS=$(whiptail --title "Selección de componentes" \
--checklist "Seleccione los componentes a respaldar:" 20 78 7 \
"${options[@]}" 3>&1 1>&2 2>&3)
else
echo "Selección de componentes a respaldar:"
for ((i=0; i<${#options[@]}; i+=3)); do
echo "${options[i]}. ${options[i+1]}"
done
read -p "Ingrese los números de los componentes a respaldar (separados por espacio): " -a selections
for sel in "${selections[@]}"; do
SELECTED_OPTIONS+="$sel "
done
fi
for option in $SELECTED_OPTIONS; do
case $option in
1)
cp -r /etc/pve "$BACKUP_PATH/etc_pve" || warning "Error al copiar /etc/pve"
cp /etc/hostname "$BACKUP_PATH/etc_hostname" || warning "Error al copiar /etc/hostname"
cp /etc/hosts "$BACKUP_PATH/etc_hosts" || warning "Error al copiar /etc/hosts"
;;
2)
cp /etc/pve/storage.cfg "$BACKUP_PATH/storage.cfg" || warning "Error al copiar storage.cfg"
;;
3)
cp -r /etc/network "$BACKUP_PATH/etc_network" || warning "Error al copiar /etc/network"
;;
4)
cp /etc/pve/user.cfg "$BACKUP_PATH/user.cfg" || warning "Error al copiar user.cfg"
cp /etc/pve/groups.cfg "$BACKUP_PATH/groups.cfg" || warning "Error al copiar groups.cfg"
;;
5)
mkdir -p "$BACKUP_PATH/var_log"
cp /var/log/pveam.log "$BACKUP_PATH/var_log/" || warning "Error al copiar pveam.log"
cp /var/log/pvedaemon.log "$BACKUP_PATH/var_log/" || warning "Error al copiar pvedaemon.log"
;;
6)
cp -r /etc/pve/firewall "$BACKUP_PATH/firewall" || warning "Error al copiar configuraciones de firewall"
;;
7)
mkdir -p "$BACKUP_PATH/vms_metadata"
for vmid in $(qm list | awk '{if(NR>1) print $1}'); do
qm config $vmid > "$BACKUP_PATH/vms_metadata/vm_${vmid}.conf"
done
mkdir -p "$BACKUP_PATH/cts_metadata"
for ctid in $(pct list | awk '{if(NR>1) print $1}'); do
pct config $ctid > "$BACKUP_PATH/cts_metadata/ct_${ctid}.conf"
done
;;
esac
done
log "Copia de seguridad personalizada completada."
}
# Función para restaurar desde una copia de seguridad
perform_restore() {
local BACKUP_LOCATION
if [ "$INTERFACE_MODE" = "whiptail" ]; then
BACKUP_LOCATION=$(whiptail --inputbox "Introduzca la ruta de la copia de seguridad:" 8 78 "/root/proxmox_backups" --title "Restauración" 3>&1 1>&2 2>&3)
else
read -p "Introduzca la ruta de la copia de seguridad: " BACKUP_LOCATION
fi
local backups=($(ls -d ${BACKUP_LOCATION}/proxmox_backup_* 2>/dev/null))
if [ ${#backups[@]} -eq 0 ]; then
error "No se encontraron copias de seguridad en $BACKUP_LOCATION"
fi
local SELECTED_BACKUP
if [ "$INTERFACE_MODE" = "whiptail" ]; then
local options=()
for i in "${!backups[@]}"; do
options+=("$i" "$(basename ${backups[$i]})")
done
SELECTED_BACKUP=$(whiptail --title "Seleccionar copia de seguridad" \
--menu "Elija la copia de seguridad a restaurar:" 20 78 10 \
"${options[@]}" 3>&1 1>&2 2>&3)
else
echo "Copias de seguridad disponibles:"
for i in "${!backups[@]}"; do
echo "$i. $(basename ${backups[$i]})"
done
read -p "Elija la copia de seguridad a restaurar: " SELECTED_BACKUP
fi
local BACKUP_PATH="${backups[$SELECTED_BACKUP]}"
# Verificar compatibilidad de versiones
if ! verify_pve_version "$BACKUP_PATH"; then
if [ "$INTERFACE_MODE" = "whiptail" ]; then
if ! whiptail --yesno "La versión de Proxmox es diferente. ¿Desea continuar con la restauración?" 8 78; then
error "Restauración cancelada debido a diferencia de versiones."
fi
else
read -p "La versión de Proxmox es diferente. ¿Desea continuar con la restauración? (s/n): " response
if [[ ! $response =~ ^[Ss]$ ]]; then
error "Restauración cancelada debido a diferencia de versiones."
fi
fi
fi
# Verificar cambios de hardware
if ! verify_hardware "$BACKUP_PATH"; then
warning "Se detectaron cambios en el hardware. Revise el informe en $BACKUP_PATH/hardware_changes.txt"
fi
# Verificar y ajustar paths de almacenamiento
if ! adjust_storage_paths "$BACKUP_PATH"; then
warning "Algunas rutas de almacenamiento han cambiado. Revise y ajuste manualmente si es necesario."
fi
# Realizar la restauración
restore_files "$BACKUP_PATH"
success "Restauración completada. Se recomienda reiniciar el sistema."
}
# Función para verificar la versión de Proxmox
verify_pve_version() {
local BACKUP_PATH=$1
local backup_version=$(cat "$BACKUP_PATH/pve_version.txt")
local current_version=$(pveversion | grep "pve-manager/")
if [ "$backup_version" != "$current_version" ]; then
warning "La versión de Proxmox actual ($current_version) es diferente de la versión del backup ($backup_version)."
return 1
fi
return 0
}
# Función para verificar cambios de hardware
verify_hardware() {
local BACKUP_PATH=$1
local current_hw=$(lshw -short)
local backup_hw_file="$BACKUP_PATH/hardware_info.txt"
if [ ! -f "$backup_hw_file" ]; then
warning "No se encontró información de hardware en la copia de seguridad."
return 1
fi
local backup_hw=$(cat "$backup_hw_file")
if [ "$current_hw" != "$backup_hw" ]; then
diff <(echo "$backup_hw") <(echo "$current_hw") > "$BACKUP_PATH/hardware_changes.txt"
return 1
fi
return 0
}
# Función para ajustar rutas de almacenamiento
adjust_storage_paths() {
local BACKUP_PATH=$1
local old_paths_file="$BACKUP_PATH/storage_paths.json"
local new_paths=$(pvesm status --output-format=json)
if [ ! -f "$old_paths_file" ]; then
warning "No se encontró información de rutas de almacenamiento en la copia de seguridad."
return 1
fi
local old_paths=$(cat "$old_paths_file")
if [ "$new_paths" != "$old_paths" ]; then
echo "Se detectaron cambios en las rutas de almacenamiento:"
diff <(echo "$old_paths") <(echo "$new_paths")
if [ "$INTERFACE_MODE" = "whiptail" ]; then
if whiptail --yesno "¿Desea ajustar automáticamente las rutas de almacenamiento?" 8 78; then
# Aquí iría la lógica para ajustar automáticamente las rutas
# Por ejemplo, actualizando los archivos de configuración relevantes
warning "Ajuste automático de rutas no implementado. Por favor, revise manualmente."
else
warning "Las rutas de almacenamiento deben ajustarse manualmente."
fi
else
read -p "¿Desea ajustar automáticamente las rutas de almacenamiento? (s/n): " response
if [[ $response =~ ^[Ss]$ ]]; then
# Aquí iría la lógica para ajustar automáticamente las rutas
warning "Ajuste automático de rutas no implementado. Por favor, revise manualmente."
else
warning "Las rutas de almacenamiento deben ajustarse manualmente."
fi
fi
return 1
fi
return 0
}
# Función para restaurar archivos
restore_files() {
local BACKUP_PATH=$1
log "Iniciando restauración de archivos..."
# Restaurar configuraciones de Proxmox
if [ -d "$BACKUP_PATH/etc_pve" ]; then
cp -r "$BACKUP_PATH/etc_pve"/* /etc/pve/ || warning "Error al restaurar /etc/pve"
fi
if [ -d "$BACKUP_PATH/etc_network" ]; then
cp -r "$BACKUP_PATH/etc_network"/* /etc/network/ || warning "Error al restaurar /etc/network"
fi
if [ -f "$BACKUP_PATH/etc_hostname" ]; then
cp "$BACKUP_PATH/etc_hostname" /etc/hostname || warning "Error al restaurar /etc/hostname"
fi
if [ -f "$BACKUP_PATH/etc_hosts" ]; then
cp "$BACKUP_PATH/etc_hosts" /etc/hosts || warning "Error al restaurar /etc/hosts"
fi
# Restaurar logs
if [ -d "$BACKUP_PATH/var_log" ]; then
cp "$BACKUP_PATH/var_log"/* /var/log/ || warning "Error al restaurar logs"
fi
# Restaurar configuraciones de almacenamiento
if [ -f "$BACKUP_PATH/storage.cfg" ]; then
cp "$BACKUP_PATH/storage.cfg" /etc/pve/storage.cfg || warning "Error al restaurar storage.cfg"
fi
# Restaurar configuraciones de usuarios y permisos
if [ -f "$BACKUP_PATH/user.cfg" ]; then
cp "$BACKUP_PATH/user.cfg" /etc/pve/user.cfg || warning "Error al restaurar user.cfg"
fi
if [ -f "$BACKUP_PATH/groups.cfg" ]; then
cp "$BACKUP_PATH/groups.cfg" /etc/pve/groups.cfg || warning "Error al restaurar groups.cfg"
fi
# Restaurar configuraciones de firewall
if [ -d "$BACKUP_PATH/firewall" ]; then
cp -r "$BACKUP_PATH/firewall"/* /etc/pve/firewall/ || warning "Error al restaurar configuraciones de firewall"
fi
# Restaurar metadatos de VMs y contenedores
if [ -d "$BACKUP_PATH/vms_metadata" ]; then
for conf in "$BACKUP_PATH/vms_metadata"/*.conf; do
vmid=$(basename "$conf" .conf | cut -d'_' -f2)
qm importconfig $vmid "$conf" || warning "Error al restaurar configuración de VM $vmid"
done
fi
if [ -d "$BACKUP_PATH/cts_metadata" ]; then
for conf in "$BACKUP_PATH/cts_metadata"/*.conf; do
ctid=$(basename "$conf" .conf | cut -d'_' -f2)
pct importconfig $ctid "$conf" || warning "Error al restaurar configuración de CT $ctid"
done
fi
log "Restauración de archivos completada."
}
# Iniciar el script
select_interface_mode
show_main_menu