Add PBS auto restore script for VM 103 to 203
자동화된 PBS 복구 스크립트 추가: - VM 103 백업을 VM 203으로 자동 복구 - 사용자 입력 최소화 (대부분 자동 처리) - 고정 설정: source=103, target=203, storage=local-lvm - 기존 VM 자동 삭제 및 재등록 지원 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
aede1e9197
commit
85c5f7f930
465
pbs_auto_restore_103_to_203.sh
Executable file
465
pbs_auto_restore_103_to_203.sh
Executable file
@ -0,0 +1,465 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# PBS 자동 복구 스크립트 - VM 103 → 203
|
||||
# 작성일: 2025-11-21
|
||||
# 용도: PBS 등록 → VM 103 백업 자동 복구 → VM 203으로 설치
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
# 색상
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
MAGENTA='\033[0;35m'
|
||||
NC='\033[0m'
|
||||
|
||||
# PBS 서버 설정 (자동 입력)
|
||||
PBS_SERVER="100.64.0.10"
|
||||
PBS_PORT="8007"
|
||||
PBS_USERNAME="0bin@pbs"
|
||||
PBS_PASSWORD="@Trajet6640"
|
||||
PBS_DATASTORE="PBS-DVA"
|
||||
PBS_FINGERPRINT="24:42:c6:0f:a8:1b:93:32:32:44:84:be:6a:c5:71:97:e4:4d:61:fc:a4:48:12:0c:97:3b:9f:1f:cc:b2:54:e8"
|
||||
PBS_STORAGE_NAME="PBS-Auto"
|
||||
|
||||
# 고정 복구 설정
|
||||
SOURCE_VMID="103" # 백업 소스 VM ID
|
||||
TARGET_VMID="203" # 복구할 VM ID
|
||||
TARGET_STORAGE="local-lvm" # 저장 스토리지
|
||||
BACKUP_TYPE="vm" # 백업 타입 (vm 고정)
|
||||
LATEST_SNAPSHOT="" # 최신 스냅샷 (자동 조회)
|
||||
|
||||
# 로그 함수
|
||||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
|
||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
log_step() { echo -e "${CYAN}╰─► ${NC}$1"; }
|
||||
|
||||
# 배너
|
||||
print_banner() {
|
||||
clear
|
||||
echo -e "${MAGENTA}"
|
||||
cat << "EOF"
|
||||
╔═══════════════════════════════════════════════════════════════╗
|
||||
║ ║
|
||||
║ PBS 자동 복구 스크립트 ║
|
||||
║ VM 103 → VM 203 자동 복구 ║
|
||||
║ ║
|
||||
╚═══════════════════════════════════════════════════════════════╝
|
||||
EOF
|
||||
echo -e "${NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Proxmox 환경 확인
|
||||
check_proxmox() {
|
||||
if [ ! -f /etc/pve/storage.cfg ]; then
|
||||
log_error "Proxmox VE가 설치되어 있지 않습니다."
|
||||
exit 1
|
||||
fi
|
||||
log_success "Proxmox VE 환경 확인 완료"
|
||||
}
|
||||
|
||||
# PBS 스토리지 등록
|
||||
register_pbs() {
|
||||
log_step "PBS 스토리지 등록 중..."
|
||||
|
||||
# 이미 등록되어 있는지 확인
|
||||
if pvesm status | grep -q "^${PBS_STORAGE_NAME} "; then
|
||||
log_warning "PBS 스토리지가 이미 등록되어 있습니다."
|
||||
log_info "기존 PBS 스토리지 제거 중..."
|
||||
pvesm remove "${PBS_STORAGE_NAME}" 2>/dev/null || true
|
||||
sleep 1
|
||||
fi
|
||||
|
||||
# PBS 등록
|
||||
log_info "PBS 서버 등록 중..."
|
||||
pvesm add pbs "${PBS_STORAGE_NAME}" \
|
||||
--server "${PBS_SERVER}" \
|
||||
--port "${PBS_PORT}" \
|
||||
--datastore "${PBS_DATASTORE}" \
|
||||
--username "${PBS_USERNAME}" \
|
||||
--password "${PBS_PASSWORD}" \
|
||||
--fingerprint "${PBS_FINGERPRINT}" \
|
||||
--namespace "PQ" 2>&1
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "PBS 스토리지 등록 완료!"
|
||||
else
|
||||
log_error "PBS 스토리지 등록 실패"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 연결 테스트
|
||||
log_info "PBS 연결 테스트 중..."
|
||||
if pvesm status -storage "${PBS_STORAGE_NAME}" &>/dev/null; then
|
||||
log_success "PBS 연결 성공!"
|
||||
echo ""
|
||||
pvesm status -storage "${PBS_STORAGE_NAME}"
|
||||
echo ""
|
||||
else
|
||||
log_error "PBS 연결 테스트 실패"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# PBS API 인증
|
||||
pbs_login() {
|
||||
log_info "PBS API 인증 중..."
|
||||
|
||||
curl -k -s -X POST "https://${PBS_SERVER}:${PBS_PORT}/api2/json/access/ticket" \
|
||||
-d "username=${PBS_USERNAME}" \
|
||||
-d "password=${PBS_PASSWORD}" > /tmp/pbs_auth.json
|
||||
|
||||
if [ ! -s /tmp/pbs_auth.json ]; then
|
||||
log_error "PBS API 응답 없음. 서버 연결을 확인하세요."
|
||||
return 1
|
||||
fi
|
||||
|
||||
PBS_TICKET=$(python3 -c 'import json; data=json.load(open("/tmp/pbs_auth.json")); print(data["data"]["ticket"])' 2>/dev/null)
|
||||
PBS_CSRF=$(python3 -c 'import json; data=json.load(open("/tmp/pbs_auth.json")); print(data["data"]["CSRFPreventionToken"])' 2>/dev/null)
|
||||
|
||||
if [ -z "$PBS_TICKET" ]; then
|
||||
log_error "PBS API 인증 실패. 응답:"
|
||||
cat /tmp/pbs_auth.json | head -5
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_success "PBS API 인증 완료"
|
||||
}
|
||||
|
||||
# VM 103 백업 확인
|
||||
check_backup_exists() {
|
||||
log_step "VM ${SOURCE_VMID} 백업 확인 중..."
|
||||
|
||||
curl -k -s -X GET "https://${PBS_SERVER}:${PBS_PORT}/api2/json/admin/datastore/${PBS_DATASTORE}/groups?ns=PQ" \
|
||||
-H "Cookie: PBSAuthCookie=${PBS_TICKET}" \
|
||||
-H "CSRFPreventionToken: ${PBS_CSRF}" > /tmp/pbs_groups.json
|
||||
|
||||
# VM 103 백업이 있는지 확인
|
||||
local has_backup=$(python3 << PYEOF
|
||||
import sys, json
|
||||
try:
|
||||
with open('/tmp/pbs_groups.json', 'r') as f:
|
||||
data = json.load(f)
|
||||
groups = data.get("data", [])
|
||||
|
||||
for group in groups:
|
||||
if group.get("backup-type") == "vm" and group.get("backup-id") == "${SOURCE_VMID}":
|
||||
backup_count = group.get("backup-count", 0)
|
||||
last_backup = group.get("last-backup", 0)
|
||||
|
||||
from datetime import datetime
|
||||
if last_backup > 0:
|
||||
last_str = datetime.fromtimestamp(last_backup).strftime("%Y-%m-%d %H:%M")
|
||||
else:
|
||||
last_str = "N/A"
|
||||
|
||||
print(f"VM {group.get('backup-id')} - {backup_count}개 백업 (최근: {last_str})")
|
||||
sys.exit(0)
|
||||
|
||||
print("")
|
||||
sys.exit(1)
|
||||
|
||||
except Exception as e:
|
||||
print("")
|
||||
sys.exit(1)
|
||||
PYEOF
|
||||
)
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "백업 발견: ${has_backup}"
|
||||
return 0
|
||||
else
|
||||
log_error "VM ${SOURCE_VMID} 백업을 찾을 수 없습니다"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 최신 스냅샷 조회
|
||||
get_latest_snapshot() {
|
||||
log_info "최신 백업 조회 중..."
|
||||
|
||||
curl -k -s -X GET "https://${PBS_SERVER}:${PBS_PORT}/api2/json/admin/datastore/${PBS_DATASTORE}/snapshots?backup-type=${BACKUP_TYPE}&backup-id=${SOURCE_VMID}&ns=PQ" \
|
||||
-H "Cookie: PBSAuthCookie=${PBS_TICKET}" \
|
||||
-H "CSRFPreventionToken: ${PBS_CSRF}" > /tmp/pbs_snapshots.json
|
||||
|
||||
LATEST_SNAPSHOT=$(python3 << 'PYEOF'
|
||||
import sys, json
|
||||
try:
|
||||
with open('/tmp/pbs_snapshots.json', 'r') as f:
|
||||
data = json.load(f)
|
||||
snapshots = data.get("data", [])
|
||||
|
||||
if not snapshots:
|
||||
print("")
|
||||
sys.exit(1)
|
||||
|
||||
# 가장 최근 백업 선택
|
||||
latest = max(snapshots, key=lambda x: x.get("backup-time", 0))
|
||||
backup_time = latest.get("backup-time", 0)
|
||||
|
||||
from datetime import datetime
|
||||
backup_time_str = datetime.utcfromtimestamp(backup_time).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
print(backup_time_str)
|
||||
|
||||
except Exception as e:
|
||||
print("", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
PYEOF
|
||||
)
|
||||
|
||||
if [ -z "$LATEST_SNAPSHOT" ]; then
|
||||
log_error "최신 백업을 찾을 수 없습니다"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_success "최신 백업: ${LATEST_SNAPSHOT}"
|
||||
return 0
|
||||
}
|
||||
|
||||
# 스토리지 확인
|
||||
check_storage() {
|
||||
log_step "저장 스토리지 확인 중..."
|
||||
|
||||
if pvesm status -storage "${TARGET_STORAGE}" &>/dev/null; then
|
||||
# VM 이미지를 저장할 수 있는지 확인
|
||||
if pvesm status -storage "${TARGET_STORAGE}" -content images &>/dev/null; then
|
||||
log_success "스토리지 '${TARGET_STORAGE}' 확인 완료"
|
||||
|
||||
# 스토리지 정보 표시
|
||||
local storage_info=$(pvesm status -storage "${TARGET_STORAGE}" 2>/dev/null | tail -n +2)
|
||||
local total_mb=$(echo "$storage_info" | awk '{print $4}')
|
||||
local avail_mb=$(echo "$storage_info" | awk '{print $6}')
|
||||
local usage_pct=$(echo "$storage_info" | awk '{print $7}')
|
||||
|
||||
if [ -n "$total_mb" ]; then
|
||||
local total_gb=$(echo "scale=2; $total_mb / 1024 / 1024" | bc 2>/dev/null || echo "N/A")
|
||||
local avail_gb=$(echo "scale=2; $avail_mb / 1024 / 1024" | bc 2>/dev/null || echo "N/A")
|
||||
log_info "스토리지 정보: ${total_gb}GB 총용량 | ${avail_gb}GB 여유 | ${usage_pct} 사용률"
|
||||
fi
|
||||
|
||||
return 0
|
||||
else
|
||||
log_error "스토리지 '${TARGET_STORAGE}'는 VM 이미지를 저장할 수 없습니다"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
log_error "스토리지 '${TARGET_STORAGE}'를 찾을 수 없습니다"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 복구 설정 확인
|
||||
show_restore_config() {
|
||||
echo ""
|
||||
log_info "복구 설정:"
|
||||
echo " 백업 소스: VM ${SOURCE_VMID}"
|
||||
echo " 백업 시점: ${LATEST_SNAPSHOT}"
|
||||
echo " 복구 ID: VM ${TARGET_VMID}"
|
||||
echo " 저장 위치: ${TARGET_STORAGE}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# VM 복구
|
||||
restore_backup() {
|
||||
log_step "백업 복구 중... (시간이 걸릴 수 있습니다)"
|
||||
echo ""
|
||||
|
||||
# 백업 경로 구성
|
||||
BACKUP_PATH="${PBS_STORAGE_NAME}:backup/${BACKUP_TYPE}/${SOURCE_VMID}/${LATEST_SNAPSHOT}"
|
||||
|
||||
log_info "백업 경로: ${BACKUP_PATH}"
|
||||
|
||||
# 기존 VM 확인
|
||||
if qm status ${TARGET_VMID} 2>/dev/null; then
|
||||
log_warning "VM ${TARGET_VMID}가 이미 존재합니다"
|
||||
log_info "기존 VM 삭제 중..."
|
||||
|
||||
qm stop ${TARGET_VMID} --skiplock 2>/dev/null || true
|
||||
sleep 2
|
||||
qm destroy ${TARGET_VMID} --purge --skiplock 2>/dev/null || true
|
||||
sleep 2
|
||||
|
||||
log_success "기존 VM 삭제 완료"
|
||||
fi
|
||||
|
||||
# 복구 실행
|
||||
log_info "VM 복구 시작..."
|
||||
echo ""
|
||||
|
||||
qmrestore "${BACKUP_PATH}" ${TARGET_VMID} \
|
||||
--storage ${TARGET_STORAGE} \
|
||||
--unique 1 2>&1 | tee /tmp/pbs-restore.log
|
||||
|
||||
if [ ${PIPESTATUS[0]} -eq 0 ]; then
|
||||
echo ""
|
||||
log_success "VM 복구 완료!"
|
||||
return 0
|
||||
else
|
||||
echo ""
|
||||
log_error "VM 복구 실패. 로그: /tmp/pbs-restore.log"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# VM 자동 시작 (선택)
|
||||
start_vm() {
|
||||
echo ""
|
||||
log_info "VM ${TARGET_VMID}를 바로 시작하시겠습니까?"
|
||||
read -t 10 -p "$(echo -e ${YELLOW}10초 내 입력하세요 (y/N):${NC}) " start_confirm < /dev/tty || start_confirm="N"
|
||||
echo ""
|
||||
|
||||
if [[ "$start_confirm" =~ ^[Yy]$ ]]; then
|
||||
log_info "VM 시작 중..."
|
||||
|
||||
qm start ${TARGET_VMID}
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "VM 시작 완료!"
|
||||
|
||||
sleep 3
|
||||
log_info "상태 확인 중..."
|
||||
qm status ${TARGET_VMID}
|
||||
else
|
||||
log_error "시작 실패"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
log_info "VM을 수동으로 시작하세요: qm start ${TARGET_VMID}"
|
||||
fi
|
||||
}
|
||||
|
||||
# 완료 메시지
|
||||
print_summary() {
|
||||
echo ""
|
||||
echo -e "${GREEN}"
|
||||
cat << "EOF"
|
||||
╔═══════════════════════════════════════════════════════════════╗
|
||||
║ ║
|
||||
║ ✅ 복구 완료! ║
|
||||
║ ║
|
||||
╚═══════════════════════════════════════════════════════════════╝
|
||||
EOF
|
||||
echo -e "${NC}"
|
||||
echo ""
|
||||
|
||||
log_info "복구 정보:"
|
||||
echo " 백업 소스: VM ${SOURCE_VMID}"
|
||||
echo " 백업 시점: ${LATEST_SNAPSHOT}"
|
||||
echo " 복구 ID: VM ${TARGET_VMID}"
|
||||
echo " 스토리지: ${TARGET_STORAGE}"
|
||||
echo ""
|
||||
|
||||
log_info "Proxmox Web UI:"
|
||||
local pve_ip=$(hostname -I | awk '{print $1}')
|
||||
echo " https://${pve_ip}:8006"
|
||||
echo ""
|
||||
|
||||
log_info "유용한 명령어:"
|
||||
echo " VM 상태: qm status ${TARGET_VMID}"
|
||||
echo " VM 시작: qm start ${TARGET_VMID}"
|
||||
echo " VM 중지: qm stop ${TARGET_VMID}"
|
||||
echo " VM 정보: qm config ${TARGET_VMID}"
|
||||
echo ""
|
||||
|
||||
log_info "PBS 스토리지:"
|
||||
echo " 이름: ${PBS_STORAGE_NAME}"
|
||||
echo " 제거: pvesm remove ${PBS_STORAGE_NAME}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 메인 실행
|
||||
main() {
|
||||
print_banner
|
||||
|
||||
# Proxmox 환경 확인
|
||||
check_proxmox
|
||||
echo ""
|
||||
|
||||
# PBS 스토리지 등록
|
||||
register_pbs || exit 1
|
||||
echo ""
|
||||
|
||||
# PBS API 인증
|
||||
pbs_login || exit 1
|
||||
echo ""
|
||||
|
||||
# VM 103 백업 확인
|
||||
check_backup_exists || exit 1
|
||||
echo ""
|
||||
|
||||
# 최신 스냅샷 조회
|
||||
get_latest_snapshot || exit 1
|
||||
echo ""
|
||||
|
||||
# 스토리지 확인
|
||||
check_storage || exit 1
|
||||
echo ""
|
||||
|
||||
# 복구 설정 표시
|
||||
show_restore_config
|
||||
|
||||
# 자동 복구 시작 (3초 대기)
|
||||
log_warning "3초 후 자동으로 복구를 시작합니다..."
|
||||
sleep 3
|
||||
echo ""
|
||||
|
||||
# 백업 복구
|
||||
restore_backup || exit 1
|
||||
echo ""
|
||||
|
||||
# VM 시작 (선택)
|
||||
start_vm
|
||||
echo ""
|
||||
|
||||
# 완료 메시지
|
||||
print_summary
|
||||
}
|
||||
|
||||
# 도움말
|
||||
if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "PBS 자동 복구 스크립트 - VM 103 → VM 203"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "사용법: $0"
|
||||
echo ""
|
||||
echo "이 스크립트는:"
|
||||
echo " 1. PBS 스토리지를 자동 등록"
|
||||
echo " 2. VM 103 백업 자동 조회"
|
||||
echo " 3. VM 203으로 자동 복구"
|
||||
echo ""
|
||||
echo "고정 설정:"
|
||||
echo " 소스 VM ID: 103"
|
||||
echo " 복구 VM ID: 203"
|
||||
echo " 스토리지: local-lvm"
|
||||
echo " PBS 서버: 100.64.0.10"
|
||||
echo ""
|
||||
echo "실행 예시:"
|
||||
echo " bash $0"
|
||||
echo ""
|
||||
echo "사전 준비:"
|
||||
echo " - Proxmox VE 설치 완료"
|
||||
echo " - PBS 서버 (100.64.0.10) 접근 가능"
|
||||
echo " - python3 설치 (보통 기본 설치됨)"
|
||||
echo ""
|
||||
echo "모든 설정이 자동화되어 있어 사용자 입력이 최소화됩니다!"
|
||||
echo ""
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Root 권한 확인
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
log_error "이 스크립트는 root 권한이 필요합니다."
|
||||
log_info "다음 명령으로 실행하세요: sudo $0"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 스크립트 실행
|
||||
main
|
||||
Loading…
Reference in New Issue
Block a user