#!/bin/bash # Цвета для вывода VERSION='1.0' RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color echo -e "${GREEN}Proxmox VM/LXC Backup script${NC}, (c) Karel Wintersky, 2025" # Функция вывода справки print_usage() { echo -e "${YELLOW}Использование:${NC} $0 [OPTIONS]" echo -e "${YELLOW}Примеры:${NC}" echo " $0 100 --mode stop --compress zstd --storage migration" echo " $0 101 --mode snapshot --storage backup" echo echo -e "${YELLOW}Параметры:${NC}" echo " --mode MODE Режим бэкапа (stop, snapshot). По умолчанию: stop" echo " --compress ALG Алгоритм сжатия (zstd, gzip, lzo). По умолчанию: zstd" echo " --storage NAME Имя хранилища. Если не указано, будет использовано хранилище по умолчанию" echo echo -e "${YELLOW}Storages:${NC}" print_pvesm_free_space echo exit 1 } function print_pvesm_free_space() { # Получаем данные один раз и сохраняем local pvesm_output pvesm_output=$(pvesm status | tail -n +2) # Определяем максимальную длину имени хранилища local max_len=0 while read -r line; do local storage_name storage_name=$(awk '{print $1}' <<< "$line") if (( ${#storage_name} > max_len )); then max_len=${#storage_name} fi done <<< "$pvesm_output" ((max_len+=2)) # Выводим данные с выравниванием while read -r line; do local storage_name available_kb available_gb storage_name=$(awk '{print $1}' <<< "$line") available_kb=$(awk '{print $5}' <<< "$line") available_gb=$(bc <<< "scale=2; $available_kb / 1048576") printf " %-${max_len}s : %s GB free\n" "$storage_name" "$available_gb" done <<< "$pvesm_output" } # Проверка наличия минимального количества аргументов if [ "$#" -lt 1 ]; then echo echo -e "${RED}ОШИБКА: Не указан обязательный параметр VMID${NC}" echo print_usage fi # Установка значений по умолчанию MODE="snapshot" COMPRESS="zstd" STORAGE_NAME="local" # Парсинг остальных аргументов while [ "$#" -gt 0 ]; do case "$1" in -h|--help) print_usage exit 1 ;; --mode) MODE="$2" if [[ ! "$MODE" =~ ^(stop|snapshot)$ ]]; then echo -e "${RED}ОШИБКА: Недопустимый режим '$MODE'. Допустимые значения: stop, snapshot${NC}" exit 1 fi shift 2 ;; --compress) COMPRESS="$2" if [[ ! "$COMPRESS" =~ ^(zstd|gzip|lzo)$ ]]; then echo -e "${RED}ОШИБКА: Недопустимый алгоритм сжатия '$COMPRESS'. Допустимые значения: zstd, gzip, lzo${NC}" exit 1 fi shift 2 ;; --storage) STORAGE_NAME="$2" shift 2 ;; *) # Парсинг аргументов VMID="$1" shift # echo -e "${RED}ОШИБКА: Неизвестный параметр '$1'${NC}" # print_usage ;; esac done NODE_NAME=$(hostname) # Проверяем, что storage существует (если указано) if [ -n "$STORAGE_NAME" ] && ! pvesm status | grep -qw "^${STORAGE_NAME}"; then echo -e "${RED}ОШИБКА: Хранилище '${STORAGE_NAME}' не найдено!${NC}" echo -e "${YELLOW}Доступные хранилища:${NC}" pvesm status | awk '{print $1}' exit 1 fi # Определяем тип (qemu или lxc) и конфигурационный файл if [ -f "/etc/pve/nodes/${NODE_NAME}/qemu-server/${VMID}.conf" ]; then VM_TYPE="qemu" CONFIG_FILE="/etc/pve/nodes/${NODE_NAME}/qemu-server/${VMID}.conf" echo -e "${GREEN}Найдена виртуальная машина (QEMU) с ID ${VMID}${NC}" elif [ -f "/etc/pve/nodes/${NODE_NAME}/lxc/${VMID}.conf" ]; then VM_TYPE="lxc" CONFIG_FILE="/etc/pve/nodes/${NODE_NAME}/lxc/${VMID}.conf" echo -e "${GREEN}Найден контейнер (LXC) с ID ${VMID}${NC}" else echo -e "${RED}ОШИБКА: Не найдена VM/LXC с ID ${VMID}!${NC}" exit 1 fi # Формируем команду vzdump VZDUMP_CMD="vzdump ${VMID} --mode ${MODE} --compress ${COMPRESS}" if [ -n "$STORAGE_NAME" ]; then VZDUMP_CMD+=" --storage ${STORAGE_NAME}" echo -e "${BLUE}Используем указанное хранилище:${NC} ${GREEN}${STORAGE_NAME}${NC}" else echo -e "${BLUE}Используем хранилище по умолчанию${NC}" fi echo -e "${YELLOW}Выполняем команду:${NC} ${VZDUMP_CMD}" # Создаем бэкап и перехватываем вывод echo -e "${BLUE}Создаем бэкап ${VM_TYPE} ${VMID}...${NC}" echo -e "Режим: ${GREEN}${MODE}${NC}" echo -e "Сжатие: ${GREEN}${COMPRESS}${NC}" BACKUP_LOG=$(eval ${VZDUMP_CMD} | tee >(cat >&2) ) if [ $? -ne 0 ]; then echo -e "${RED}ОШИБКА: Не удалось создать бэкап!${NC}" echo -e "${YELLOW}Подробности:${NC}" echo "$BACKUP_LOG" exit 1 fi # Извлекаем имя файла бэкапа BACKUP_FILE=$(echo "$BACKUP_LOG" | grep -oP "creating vzdump archive '\K[^']*") if [ -z "$BACKUP_FILE" ]; then echo -e "${RED}ОШИБКА: Не удалось определить путь к бэкапу!${NC}" echo -e "${YELLOW}Полный вывод vzdump:${NC}" echo "$BACKUP_LOG" exit 1 fi # Копируем конфигурационный файл BACKUP_BASENAME=$(basename "${BACKUP_FILE}" .vma.zst) CONFIG_COPY="${BACKUP_FILE%.*}.conf" cp "${CONFIG_FILE}" "${CONFIG_COPY}" echo -e "${GREEN}Конфигурация сохранена в ${NC} ${CONFIG_COPY}${NC}" # Сохраняем заметки (комментарии и description) в .md файл # NOTES=$(grep -E '^#|^description:' "${CONFIG_FILE}" | sed 's/^#//; s/^description: //') NOTES=$(grep -E '^#|^description:' "${CONFIG_FILE}" | sed 's/^#//; s/^description: //' | sed -e 's/%3A/:/g' -e 's/%2F/\//g') if [ -n "${NOTES}" ]; then NOTES_FILE="${BACKUP_FILE%.*}.md" echo -e "## Proxmox Notes for ${VM_TYPE} ${VMID}\n\n${NOTES}" > "${NOTES_FILE}" echo -e "${GREEN}Заметки сохранены в ${NC} ${NOTES_FILE}" else echo -e "${YELLOW}Нет заметок для ${VM_TYPE} ${VMID}${NC}" fi echo -e "${GREEN}Бэкап успешно создан:${NC} ${BACKUP_FILE}"