Ultima attività 1751466336

Скрипт бэкапа проксмоксовых VM/LXC с сохранением конфигурации и примечаний к виртуалке

Revisione 18904dee91ae24d3bc5ab3f83df6e13f65a28446

vzbackup.sh Raw
1#!/bin/bash
2
3# Отличается от обычного бэкапа тем, что сохраняет комментарии отдельно, рядом в файл. И всё.
4
5# Цвета для вывода
6VERSION='1.0'
7RED='\033[0;31m'
8GREEN='\033[0;32m'
9YELLOW='\033[1;33m'
10BLUE='\033[0;34m'
11NC='\033[0m' # No Color
12
13echo -e "${GREEN}Proxmox VM/LXC Backup script${NC}, (c) Karel Wintersky, 2025"
14
15# Функция вывода справки
16print_usage() {
17 echo -e "${YELLOW}Использование:${NC} $0 <VMID> [OPTIONS]"
18 echo -e "${YELLOW}Примеры:${NC}"
19 echo " $0 100 --mode stop --compress zstd --storage migration"
20 echo " $0 101 --mode snapshot --storage backup"
21 echo
22 echo -e "${YELLOW}Параметры:${NC}"
23 echo " --mode MODE Режим бэкапа (stop, snapshot). По умолчанию: stop"
24 echo " --compress ALG Алгоритм сжатия (zstd, gzip, lzo). По умолчанию: zstd"
25 echo " --storage NAME Имя хранилища. Если не указано, будет использовано хранилище по умолчанию"
26 echo
27 echo -e "${YELLOW}Storages:${NC}"
28 print_pvesm_free_space
29 echo
30 exit 1
31}
32
33function print_pvesm_free_space() {
34 # Получаем данные один раз и сохраняем
35 local pvesm_output
36 pvesm_output=$(pvesm status | tail -n +2)
37
38 # Определяем максимальную длину имени хранилища
39 local max_len=0
40 while read -r line; do
41 local storage_name
42 storage_name=$(awk '{print $1}' <<< "$line")
43 if (( ${#storage_name} > max_len )); then
44 max_len=${#storage_name}
45 fi
46 done <<< "$pvesm_output"
47 ((max_len+=2))
48
49 # Выводим данные с выравниванием
50 while read -r line; do
51 local storage_name available_kb available_gb
52 storage_name=$(awk '{print $1}' <<< "$line")
53 available_kb=$(awk '{print $5}' <<< "$line")
54 available_gb=$(bc <<< "scale=2; $available_kb / 1048576")
55
56 printf " %-${max_len}s : %s GB free\n" "$storage_name" "$available_gb"
57 done <<< "$pvesm_output"
58}
59
60# Проверка наличия минимального количества аргументов
61if [ "$#" -lt 1 ]; then
62 echo
63 echo -e "${RED}ОШИБКА: Не указан обязательный параметр VMID${NC}"
64 echo
65 print_usage
66fi
67
68# Установка значений по умолчанию
69MODE="snapshot"
70COMPRESS="zstd"
71STORAGE_NAME="local"
72
73# Парсинг остальных аргументов
74while [ "$#" -gt 0 ]; do
75 case "$1" in
76 -h|--help)
77 print_usage
78 exit 1
79 ;;
80 --mode)
81 MODE="$2"
82 if [[ ! "$MODE" =~ ^(stop|snapshot)$ ]]; then
83 echo -e "${RED}ОШИБКА: Недопустимый режим '$MODE'. Допустимые значения: stop, snapshot${NC}"
84 exit 1
85 fi
86 shift 2
87 ;;
88 --compress)
89 COMPRESS="$2"
90 if [[ ! "$COMPRESS" =~ ^(zstd|gzip|lzo)$ ]]; then
91 echo -e "${RED}ОШИБКА: Недопустимый алгоритм сжатия '$COMPRESS'. Допустимые значения: zstd, gzip, lzo${NC}"
92 exit 1
93 fi
94 shift 2
95 ;;
96 --storage)
97 STORAGE_NAME="$2"
98 shift 2
99 ;;
100 *)
101 # Парсинг аргументов
102 VMID="$1"
103 shift
104
105 # echo -e "${RED}ОШИБКА: Неизвестный параметр '$1'${NC}"
106 # print_usage
107 ;;
108 esac
109done
110
111NODE_NAME=$(hostname)
112
113# Проверяем, что storage существует (если указано)
114if [ -n "$STORAGE_NAME" ] && ! pvesm status | grep -qw "^${STORAGE_NAME}"; then
115 echo -e "${RED}ОШИБКА: Хранилище '${STORAGE_NAME}' не найдено!${NC}"
116 echo -e "${YELLOW}Доступные хранилища:${NC}"
117 pvesm status | awk '{print $1}'
118 exit 1
119fi
120
121# Определяем тип (qemu или lxc) и конфигурационный файл
122if [ -f "/etc/pve/nodes/${NODE_NAME}/qemu-server/${VMID}.conf" ]; then
123 VM_TYPE="qemu"
124 CONFIG_FILE="/etc/pve/nodes/${NODE_NAME}/qemu-server/${VMID}.conf"
125 echo -e "${GREEN}Найдена виртуальная машина (QEMU) с ID ${VMID}${NC}"
126elif [ -f "/etc/pve/nodes/${NODE_NAME}/lxc/${VMID}.conf" ]; then
127 VM_TYPE="lxc"
128 CONFIG_FILE="/etc/pve/nodes/${NODE_NAME}/lxc/${VMID}.conf"
129 echo -e "${GREEN}Найден контейнер (LXC) с ID ${VMID}${NC}"
130else
131 echo -e "${RED}ОШИБКА: Не найдена VM/LXC с ID ${VMID}!${NC}"
132 exit 1
133fi
134
135# Формируем команду vzdump
136VZDUMP_CMD="vzdump ${VMID} --mode ${MODE} --compress ${COMPRESS}"
137if [ -n "$STORAGE_NAME" ]; then
138 VZDUMP_CMD+=" --storage ${STORAGE_NAME}"
139 echo -e "${BLUE}Используем указанное хранилище:${NC} ${GREEN}${STORAGE_NAME}${NC}"
140else
141 echo -e "${BLUE}Используем хранилище по умолчанию${NC}"
142fi
143
144echo -e "${YELLOW}Выполняем команду:${NC} ${VZDUMP_CMD}"
145
146# Создаем бэкап и перехватываем вывод
147echo -e "${BLUE}Создаем бэкап ${VM_TYPE} ${VMID}...${NC}"
148echo -e "Режим: ${GREEN}${MODE}${NC}"
149echo -e "Сжатие: ${GREEN}${COMPRESS}${NC}"
150
151BACKUP_LOG=$(eval ${VZDUMP_CMD} | tee >(cat >&2) )
152
153if [ $? -ne 0 ]; then
154 echo -e "${RED}ОШИБКА: Не удалось создать бэкап!${NC}"
155 echo -e "${YELLOW}Подробности:${NC}"
156 echo "$BACKUP_LOG"
157 exit 1
158fi
159
160# Извлекаем имя файла бэкапа
161BACKUP_FILE=$(echo "$BACKUP_LOG" | grep -oP "creating vzdump archive '\K[^']*")
162
163if [ -z "$BACKUP_FILE" ]; then
164 echo -e "${RED}ОШИБКА: Не удалось определить путь к бэкапу!${NC}"
165 echo -e "${YELLOW}Полный вывод vzdump:${NC}"
166 echo "$BACKUP_LOG"
167 exit 1
168fi
169
170# Копируем конфигурационный файл
171BACKUP_BASENAME=$(basename "${BACKUP_FILE}" .vma.zst)
172CONFIG_COPY="${BACKUP_FILE%.*}.conf"
173cp "${CONFIG_FILE}" "${CONFIG_COPY}"
174echo -e "${GREEN}Конфигурация сохранена в ${NC} ${CONFIG_COPY}${NC}"
175
176# Сохраняем заметки (комментарии и description) в .md файл
177# NOTES=$(grep -E '^#|^description:' "${CONFIG_FILE}" | sed 's/^#//; s/^description: //')
178NOTES=$(grep -E '^#|^description:' "${CONFIG_FILE}" | sed 's/^#//; s/^description: //' | sed -e 's/%3A/:/g' -e 's/%2F/\//g')
179if [ -n "${NOTES}" ]; then
180 NOTES_FILE="${BACKUP_FILE%.*}.md"
181 echo -e "## Proxmox Notes for ${VM_TYPE} ${VMID}\n\n${NOTES}" > "${NOTES_FILE}"
182 echo -e "${GREEN}Заметки сохранены в ${NC} ${NOTES_FILE}"
183else
184 echo -e "${YELLOW}Нет заметок для ${VM_TYPE} ${VMID}${NC}"
185fi
186
187echo -e "${GREEN}Бэкап успешно создан:${NC} ${BACKUP_FILE}"
188