From 85c5f7f930415ec1f8fd82934f5991ccdad2bf09 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 21 Nov 2025 14:17:39 +0000 Subject: [PATCH] Add PBS auto restore script for VM 103 to 203 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 자동화된 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 --- pbs_auto_restore_103_to_203.sh | 465 +++++++++++++++++++++++++++++++++ 1 file changed, 465 insertions(+) create mode 100755 pbs_auto_restore_103_to_203.sh diff --git a/pbs_auto_restore_103_to_203.sh b/pbs_auto_restore_103_to_203.sh new file mode 100755 index 0000000..660bd27 --- /dev/null +++ b/pbs_auto_restore_103_to_203.sh @@ -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