From 070a23ff74e8b6410e5e77ada15e621ccecea384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=9C=EA=B3=A8=EC=95=BD=EC=82=AC?= Date: Tue, 28 Oct 2025 09:00:07 +0000 Subject: [PATCH] =?UTF-8?q?=EC=B4=88=EA=B8=B0=20=EC=BB=A4=EB=B0=8B:=20PBS?= =?UTF-8?q?=20=EB=B0=B1=EC=97=85=20=EB=B3=B5=EA=B5=AC=20=EC=8A=A4=ED=81=AC?= =?UTF-8?q?=EB=A6=BD=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✨ 주요 기능: - PBS (Proxmox Backup Server)에서 VM/Container 백업 복구 - 대화형 모드로 쉬운 백업 선택 및 복구 - 백업 목록 조회 및 VMID별 검색 - VM 및 Container 타입 자동 감지 - VMID 중복 확인 및 다른 VMID로 복구 (클론) - 스토리지 선택 가능 - 안전한 확인 절차 포함 🛠️ 포함 파일: - pbs-restore.sh: PBS 백업 복구 스크립트 - README.md: 상세한 사용 가이드, 시나리오 및 문제 해결 📦 사용 방법: # 대화형 모드 (권장) curl -fsSL https://git.0bin.in/thug0bin/pve-pbs-restore/raw/branch/main/pbs-restore.sh | bash # 백업 목록 보기 ./pbs-restore.sh -l -s pbs-backup -v 100 # 비대화형 복구 ./pbs-restore.sh -r -V "pbs-backup:backup/vm-100-2025_01_28-10_00_00.vma.zst" -t 100 -T vm 🔧 주요 기능: - qmrestore 및 pct restore 명령어 활용 - 컬러 출력으로 가독성 향상 - Volume ID 자동 파싱 - 백업 날짜 및 크기 표시 - 복구 전 확인 단계 - Root 권한 및 Proxmox VE 환경 자동 확인 🎯 사용 사례: - 삭제된 VM/Container 복구 - VM 클론 생성 (테스트 환경) - 특정 시점으로 롤백 - 재해 복구 (Disaster Recovery) - 자동화 스크립트에 통합 💡 특징: - 3가지 모드: 대화형, 목록, 복구 - VMID 가용성 자동 확인 - 다중 PBS 스토리지 지원 - 추가 복구 옵션 지원 (--unique, --force 등) - 상세한 에러 메시지 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- README.md | 460 +++++++++++++++++++++++++++++++++++++++++++++ pbs-restore.sh | 494 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 954 insertions(+) create mode 100644 README.md create mode 100644 pbs-restore.sh diff --git a/README.md b/README.md new file mode 100644 index 0000000..3d2373c --- /dev/null +++ b/README.md @@ -0,0 +1,460 @@ +# PBS 백업 복구 스크립트 + +Proxmox Backup Server(PBS)에서 VM 및 Container 백업을 쉽고 빠르게 복구하는 스크립트입니다. + +## 특징 + +- ✅ 대화형 모드로 쉬운 백업 선택 및 복구 +- ✅ 백업 목록 조회 및 검색 +- ✅ VM 및 Container 자동 감지 +- ✅ VMID 중복 확인 +- ✅ 다른 VMID로 복구 가능 (클론) +- ✅ 스토리지 선택 가능 +- ✅ 컬러 출력으로 가독성 향상 +- ✅ 안전한 확인 절차 + +## 요구사항 + +- Proxmox VE 7.0 이상 +- PBS 스토리지가 PVE에 추가되어 있어야 함 +- Root 권한 + +## 빠른 설치 + +### 방법 1: 원라인 실행 (대화형 모드) + +```bash +curl -fsSL https://git.0bin.in/thug0bin/pve-pbs-restore/raw/branch/main/pbs-restore.sh | bash +``` + +### 방법 2: 다운로드 후 실행 + +```bash +wget https://git.0bin.in/thug0bin/pve-pbs-restore/raw/branch/main/pbs-restore.sh +chmod +x pbs-restore.sh +./pbs-restore.sh +``` + +## 사용 방법 + +### 1. 대화형 모드 (가장 쉬움, 권장) + +스크립트를 실행하면 단계별로 안내합니다: + +```bash +./pbs-restore.sh +``` + +**진행 순서:** +1. PBS 스토리지 선택 +2. 복구할 원본 VMID 입력 +3. 백업 목록에서 선택 +4. 타겟 VMID 입력 (같은 VMID 또는 새로운 VMID) +5. 복구할 스토리지 선택 +6. 확인 후 복구 진행 + +### 2. 백업 목록 보기 + +#### 모든 백업 보기 + +```bash +./pbs-restore.sh -l -s pbs-backup +``` + +#### 특정 VMID의 백업만 보기 + +```bash +./pbs-restore.sh -l -s pbs-backup -v 100 +``` + +### 3. 비대화형 복구 (자동화) + +#### VM 복구 (같은 VMID로) + +```bash +./pbs-restore.sh -r \ + -V "pbs-backup:backup/vm-100-2025_01_28-10_00_00.vma.zst" \ + -t 100 \ + -T vm +``` + +#### VM 복구 (다른 VMID로 - 클론) + +```bash +./pbs-restore.sh -r \ + -V "pbs-backup:backup/vm-100-2025_01_28-10_00_00.vma.zst" \ + -t 150 \ + -T vm \ + -S local-lvm +``` + +#### Container 복구 + +```bash +./pbs-restore.sh -r \ + -V "pbs-backup:backup/ct-200-2025_01_28-10_00_00.tar.zst" \ + -t 200 \ + -T ct +``` + +#### 추가 옵션과 함께 복구 + +```bash +./pbs-restore.sh -r \ + -V "pbs-backup:backup/vm-100-2025_01_28-10_00_00.vma.zst" \ + -t 100 \ + -T vm \ + -S local-lvm \ + -o "--unique --force" +``` + +## 명령어 옵션 + +### 모드 선택 + +| 옵션 | 설명 | 필수 | +|------|------|------| +| (없음) | 대화형 모드 실행 | - | +| `-l` | 백업 목록 보기 모드 | - | +| `-r` | 백업 복구 모드 | - | +| `-h` | 도움말 표시 | - | + +### 백업 목록 모드 옵션 + +| 옵션 | 설명 | 필수 | +|------|------|------| +| `-s STORAGE` | PBS 스토리지 이름 | ✓ | +| `-v VMID` | 특정 VMID의 백업만 검색 | ✗ | + +### 복구 모드 옵션 + +| 옵션 | 설명 | 필수 | +|------|------|------| +| `-V VOLID` | 복구할 Volume ID | ✓ | +| `-t VMID` | 타겟 VMID | ✓ | +| `-T TYPE` | 백업 타입 (vm/ct) | ✗ (자동 감지) | +| `-S STORAGE` | 복구할 스토리지 | ✗ | +| `-o OPTIONS` | 추가 옵션 | ✗ | + +## 사용 시나리오 + +### 시나리오 1: 삭제된 VM 복구 + +VM을 실수로 삭제한 경우 원래 VMID로 복구: + +```bash +./pbs-restore.sh +# 대화형 모드에서: +# 1. PBS 스토리지 선택 +# 2. 삭제된 VMID 입력 +# 3. 최신 백업 선택 +# 4. 같은 VMID로 복구 +``` + +### 시나리오 2: VM 클론 (테스트용) + +프로덕션 VM을 다른 VMID로 복구하여 테스트 환경 구축: + +```bash +./pbs-restore.sh -r \ + -V "pbs-backup:backup/vm-100-2025_01_28-10_00_00.vma.zst" \ + -t 999 \ + -T vm \ + -S local-lvm +``` + +### 시나리오 3: 특정 시점으로 롤백 + +특정 날짜의 백업으로 복구: + +```bash +# 1. 백업 목록에서 날짜 확인 +./pbs-restore.sh -l -s pbs-backup -v 100 + +# 2. 원하는 날짜의 백업 선택하여 복구 +./pbs-restore.sh -r \ + -V "pbs-backup:backup/vm-100-2025_01_20-02_00_00.vma.zst" \ + -t 100 \ + -T vm +``` + +### 시나리오 4: Container 복구 + +LXC Container 복구: + +```bash +./pbs-restore.sh +# Container VMID 입력 +# 백업 선택 +# 복구 진행 +``` + +### 시나리오 5: 재해 복구 (Disaster Recovery) + +서버 장애 후 전체 VM 복구: + +```bash +# 각 VM에 대해 반복 +for vmid in 100 101 102 200 201; do + ./pbs-restore.sh -l -s pbs-backup -v $vmid + # 최신 백업 확인 후 복구 +done +``` + +## Volume ID 형식 + +PBS의 Volume ID는 다음과 같은 형식을 가집니다: + +``` +STORAGE:backup/TYPE-VMID-DATE.EXTENSION + +예시: +pbs-backup:backup/vm-100-2025_01_28-10_00_00.vma.zst +pbs-backup:backup/ct-200-2025_01_28-15_30_00.tar.zst +``` + +**구성 요소:** +- `STORAGE`: PBS 스토리지 이름 +- `TYPE`: `vm` (가상머신) 또는 `ct` (컨테이너) +- `VMID`: VM/CT ID +- `DATE`: 백업 날짜 및 시간 +- `EXTENSION`: 압축 형식 (`.vma.zst`, `.tar.zst` 등) + +## 복구 옵션 상세 + +### VM 복구 옵션 (qmrestore) + +| 옵션 | 설명 | 예시 | +|------|------|------| +| `--storage` | 복구할 스토리지 | `--storage local-lvm` | +| `--unique` | MAC 주소 및 UUID 재생성 | `--unique` | +| `--force` | 기존 VM 덮어쓰기 | `--force` | +| `--pool` | 리소스 풀 지정 | `--pool production` | + +예시: +```bash +./pbs-restore.sh -r \ + -V "pbs-backup:backup/vm-100-2025_01_28-10_00_00.vma.zst" \ + -t 150 \ + -T vm \ + -o "--unique --pool test" +``` + +### Container 복구 옵션 (pct restore) + +| 옵션 | 설명 | 예시 | +|------|------|------| +| `--storage` | 복구할 스토리지 | `--storage local-lvm` | +| `--force` | 기존 CT 덮어쓰기 | `--force` | +| `--unprivileged` | 비특권 컨테이너로 복구 | `--unprivileged 1` | +| `--pool` | 리소스 풀 지정 | `--pool production` | + +예시: +```bash +./pbs-restore.sh -r \ + -V "pbs-backup:backup/ct-200-2025_01_28-10_00_00.tar.zst" \ + -t 201 \ + -T ct \ + -o "--unprivileged 1 --force" +``` + +## PBS 스토리지 추가 + +PBS 스토리지가 아직 추가되지 않았다면, 먼저 추가해야 합니다: + +```bash +# PBS 스토리지 추가 스크립트 사용 +curl -fsSL https://git.0bin.in/thug0bin/pve-add-pbs-storage/raw/branch/main/add-pbs-storage.sh | bash +``` + +또는 수동으로: + +```bash +pvesm add pbs pbs-backup \ + --server 192.168.1.100 \ + --datastore datastore1 \ + --username "root@pam!backup" \ + --password "your-token-secret" +``` + +## 복구 후 확인 + +복구가 완료된 후 다음 명령어로 확인: + +### VM 확인 + +```bash +# VM 상태 확인 +qm status 100 + +# VM 설정 확인 +qm config 100 + +# VM 시작 +qm start 100 +``` + +### Container 확인 + +```bash +# CT 상태 확인 +pct status 200 + +# CT 설정 확인 +pct config 200 + +# CT 시작 +pct start 200 +``` + +## 문제 해결 + +### 백업을 찾을 수 없음 + +```bash +# PBS 스토리지 연결 확인 +pvesm status --storage pbs-backup + +# PBS 스토리지 컨텐츠 확인 +pvesm list pbs-backup +``` + +### VMID가 이미 사용 중 + +```bash +# 기존 VM/CT 삭제 +qm destroy 100 +# 또는 +pct destroy 200 + +# 또는 다른 VMID로 복구 +./pbs-restore.sh -r -V "..." -t 999 -T vm +``` + +### 복구 실패 - 스토리지 공간 부족 + +```bash +# 스토리지 공간 확인 +pvesm status + +# 다른 스토리지로 복구 +./pbs-restore.sh -r -V "..." -t 100 -T vm -S other-storage +``` + +### 복구 실패 - 권한 문제 + +```bash +# Root로 실행 확인 +sudo ./pbs-restore.sh + +# PBS API Token 권한 확인 (PBS 웹 UI) +``` + +### Volume ID를 찾을 수 없음 + +```bash +# 올바른 형식 확인 +pvesm list pbs-backup | grep vm-100 + +# 복사한 Volume ID 사용 +./pbs-restore.sh -r \ + -V "pbs-backup:backup/vm-100-2025_01_28-10_00_00.vma.zst" \ + -t 100 -T vm +``` + +## 고급 사용법 + +### 스크립트로 자동화 + +```bash +#!/bin/bash +# 자동 복구 스크립트 예시 + +PBS_STORAGE="pbs-backup" +VMID_LIST=(100 101 102) + +for vmid in "${VMID_LIST[@]}"; do + echo "Checking backups for VMID: $vmid" + + # 최신 백업 가져오기 + latest_backup=$(pvesm list "$PBS_STORAGE" | \ + grep -E "vm-${vmid}-" | \ + tail -1 | \ + awk '{print $1}') + + if [ -n "$latest_backup" ]; then + echo "Restoring: $latest_backup" + ./pbs-restore.sh -r -V "$latest_backup" -t "$vmid" -T vm + fi +done +``` + +### 특정 기간의 백업 찾기 + +```bash +# 특정 날짜의 백업 검색 +./pbs-restore.sh -l -s pbs-backup -v 100 | grep "2025-01-28" + +# 최근 7일 백업 검색 +pvesm list pbs-backup | grep "vm-100" | tail -7 +``` + +### 병렬 복구 (여러 VM 동시 복구) + +```bash +#!/bin/bash +# 여러 VM을 동시에 복구 (신중하게 사용!) + +restore_vm() { + local volid="$1" + local vmid="$2" + ./pbs-restore.sh -r -V "$volid" -t "$vmid" -T vm & +} + +restore_vm "pbs-backup:backup/vm-100-2025_01_28-10_00_00.vma.zst" 100 +restore_vm "pbs-backup:backup/vm-101-2025_01_28-10_00_00.vma.zst" 101 +restore_vm "pbs-backup:backup/vm-102-2025_01_28-10_00_00.vma.zst" 102 + +wait +echo "모든 복구 완료" +``` + +## 백업 전략 권장사항 + +1. **정기 백업**: 자동 백업 스케줄 설정 +2. **보존 정책**: 적절한 백업 보관 기간 설정 +3. **복구 테스트**: 정기적으로 복구 테스트 수행 +4. **문서화**: 중요 VM의 복구 절차 문서화 +5. **오프사이트 백업**: PBS Remote Sync로 원격 백업 + +## 보안 고려사항 + +- ✅ Root 권한으로만 실행 +- ✅ PBS API Token 사용 권장 +- ✅ 복구 전 항상 확인 단계 포함 +- ✅ 중요 VM은 복구 테스트 수행 +- ✅ 백업 암호화 사용 권장 + +## 참고 자료 + +- [Proxmox VE Backup and Restore](https://pve.proxmox.com/wiki/Backup_and_Restore) +- [Proxmox Backup Server 공식 문서](https://pbs.proxmox.com/docs/) +- [qmrestore 매뉴얼](https://pve.proxmox.com/pve-docs/qmrestore.1.html) +- [pct restore 매뉴얼](https://pve.proxmox.com/pve-docs/pct.1.html) + +## 라이선스 + +MIT License + +## 기여 + +Issues 및 Pull Requests는 언제나 환영합니다! + +## 관련 프로젝트 + +- [pve-add-pbs-storage](https://git.0bin.in/thug0bin/pve-add-pbs-storage) - PBS 스토리지 추가 스크립트 +- [pve9-repo-fix](https://git.0bin.in/thug0bin/pve9-repo-fix) - Proxmox VE 9.0 저장소 수정 스크립트 + +## 지원 + +문제가 발생하면 GitHub Issues에 보고해주세요. diff --git a/pbs-restore.sh b/pbs-restore.sh new file mode 100644 index 0000000..6ebb33c --- /dev/null +++ b/pbs-restore.sh @@ -0,0 +1,494 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Proxmox VE - PBS 백업 복구 스크립트 +# PBS에서 VM/Container 백업을 쉽게 복구 + +# 색상 정의 +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' # No Color + +# 로그 함수 +log_info() { echo -e "${BLUE}ℹ${NC} $1"; } +log_success() { echo -e "${GREEN}✓${NC} $1"; } +log_warning() { echo -e "${YELLOW}⚠${NC} $1"; } +log_error() { echo -e "${RED}✗${NC} $1"; } +log_step() { echo -e "${CYAN}➜${NC} $1"; } + +# 배너 출력 +print_banner() { + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo " PBS 백업 복구 스크립트" + echo " Proxmox Backup Server Restore Tool" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" +} + +# Root 권한 확인 +check_root() { + if [[ $EUID -ne 0 ]]; then + log_error "이 스크립트는 root 권한이 필요합니다." + exit 1 + fi +} + +# Proxmox VE 환경 확인 +check_proxmox() { + if ! command -v qmrestore &> /dev/null && ! command -v pct &> /dev/null; then + log_error "Proxmox VE가 설치되어 있지 않습니다." + exit 1 + fi + log_success "Proxmox VE 환경 확인 완료" +} + +# PBS 스토리지 목록 가져오기 +list_pbs_storages() { + log_step "사용 가능한 PBS 스토리지 목록:" + echo "" + pvesm status --type pbs 2>/dev/null | tail -n +2 | awk '{print " • " $1}' + echo "" +} + +# PBS 스토리지에서 백업 목록 가져오기 +list_backups() { + local storage="$1" + log_step "PBS 스토리지 '${storage}'의 백업 목록을 가져오는 중..." + echo "" + + # 백업 목록 가져오기 + local backups=$(pvesm list "$storage" 2>/dev/null | grep -E '\.vma|\.tar' || true) + + if [[ -z "$backups" ]]; then + log_warning "백업을 찾을 수 없습니다." + return 1 + fi + + # 백업 목록 파싱 및 출력 + echo "$backups" | while IFS= read -r line; do + local volid=$(echo "$line" | awk '{print $1}') + local type=$(echo "$line" | awk '{print $2}') + local size=$(echo "$line" | awk '{print $3}') + + # VMID 추출 + local vmid=$(echo "$volid" | grep -oP 'vm-\K[0-9]+' || echo "N/A") + if [[ "$vmid" == "N/A" ]]; then + vmid=$(echo "$volid" | grep -oP 'ct-\K[0-9]+' || echo "N/A") + fi + + # 날짜 추출 시도 + local date=$(echo "$volid" | grep -oP '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z' || echo "") + + echo -e "${CYAN}VMID:${NC} $vmid ${MAGENTA}Type:${NC} $type ${YELLOW}Size:${NC} $size" + echo " Volume ID: $volid" + if [[ -n "$date" ]]; then + echo " Date: $date" + fi + echo "" + done +} + +# 특정 VMID의 백업 목록 가져오기 +list_backups_by_vmid() { + local storage="$1" + local vmid="$2" + + log_step "VMID ${vmid}의 백업 목록을 검색 중..." + echo "" + + local backups=$(pvesm list "$storage" 2>/dev/null | grep -E "vm-${vmid}-|ct-${vmid}-" || true) + + if [[ -z "$backups" ]]; then + log_warning "VMID ${vmid}의 백업을 찾을 수 없습니다." + return 1 + fi + + local count=0 + declare -g -A BACKUP_LIST + + echo "$backups" | while IFS= read -r line; do + ((count++)) + local volid=$(echo "$line" | awk '{print $1}') + local type=$(echo "$line" | awk '{print $2}') + local size=$(echo "$line" | awk '{print $3}') + local date=$(echo "$volid" | grep -oP '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z' || echo "Unknown") + + BACKUP_LIST[$count]="$volid" + + echo -e "${GREEN}[$count]${NC} ${CYAN}Date:${NC} $date ${MAGENTA}Type:${NC} $type ${YELLOW}Size:${NC} $size" + echo " Volume ID: $volid" + echo "" + done + + return 0 +} + +# 백업 검색 (대화형) +search_backups_interactive() { + list_pbs_storages + + read -p "PBS 스토리지 이름을 입력하세요: " PBS_STORAGE + + if ! pvesm status --storage "$PBS_STORAGE" &> /dev/null; then + log_error "스토리지 '${PBS_STORAGE}'를 찾을 수 없습니다." + exit 1 + fi + + echo "" + read -p "복구할 VMID를 입력하세요 (모두 보려면 Enter): " SEARCH_VMID + + echo "" + if [[ -n "$SEARCH_VMID" ]]; then + list_backups_by_vmid "$PBS_STORAGE" "$SEARCH_VMID" + else + list_backups "$PBS_STORAGE" + fi +} + +# 백업 복구 (VM) +restore_vm() { + local volid="$1" + local target_vmid="$2" + local storage="$3" + local options="$4" + + log_step "VM 백업 복구 시작..." + log_info "Volume ID: $volid" + log_info "Target VMID: $target_vmid" + log_info "Storage: $storage" + + # 기본 복구 명령어 + local cmd="qmrestore '$volid' $target_vmid" + + # 스토리지 지정 (선택사항) + if [[ -n "$storage" ]]; then + cmd+=" --storage $storage" + fi + + # 추가 옵션 + if [[ -n "$options" ]]; then + cmd+=" $options" + fi + + log_info "실행 명령어: $cmd" + echo "" + + # 확인 + read -p "복구를 진행하시겠습니까? (y/N): " confirm + if [[ ! "$confirm" =~ ^[Yy]$ ]]; then + log_warning "복구가 취소되었습니다." + exit 0 + fi + + # 복구 실행 + echo "" + log_step "복구 진행 중..." + if eval "$cmd"; then + log_success "VM 복구가 완료되었습니다!" + log_info "VMID: $target_vmid" + return 0 + else + log_error "VM 복구에 실패했습니다." + return 1 + fi +} + +# 백업 복구 (Container) +restore_container() { + local volid="$1" + local target_vmid="$2" + local storage="$3" + local options="$4" + + log_step "Container 백업 복구 시작..." + log_info "Volume ID: $volid" + log_info "Target VMID: $target_vmid" + log_info "Storage: $storage" + + # 기본 복구 명령어 + local cmd="pct restore $target_vmid '$volid'" + + # 스토리지 지정 (선택사항) + if [[ -n "$storage" ]]; then + cmd+=" --storage $storage" + fi + + # 추가 옵션 + if [[ -n "$options" ]]; then + cmd+=" $options" + fi + + log_info "실행 명령어: $cmd" + echo "" + + # 확인 + read -p "복구를 진행하시겠습니까? (y/N): " confirm + if [[ ! "$confirm" =~ ^[Yy]$ ]]; then + log_warning "복구가 취소되었습니다." + exit 0 + fi + + # 복구 실행 + echo "" + log_step "복구 진행 중..." + if eval "$cmd"; then + log_success "Container 복구가 완료되었습니다!" + log_info "VMID: $target_vmid" + return 0 + else + log_error "Container 복구에 실패했습니다." + return 1 + fi +} + +# VMID 사용 가능 여부 확인 +check_vmid_available() { + local vmid="$1" + + if qm status "$vmid" &> /dev/null || pct status "$vmid" &> /dev/null; then + log_error "VMID ${vmid}는 이미 사용 중입니다." + log_warning "다른 VMID를 사용하거나 기존 VM/CT를 삭제하세요." + return 1 + fi + return 0 +} + +# 스토리지 목록 출력 +list_storages() { + log_step "사용 가능한 스토리지 목록:" + echo "" + pvesm status | tail -n +2 | awk '{print " • " $1 " (" $2 ")"}' + echo "" +} + +# 대화형 모드 +interactive_mode() { + echo "" + log_info "PBS 백업 복구 - 대화형 모드" + echo "" + + # PBS 스토리지 선택 + list_pbs_storages + read -p "PBS 스토리지 이름: " PBS_STORAGE + + if ! pvesm status --storage "$PBS_STORAGE" &> /dev/null; then + log_error "스토리지 '${PBS_STORAGE}'를 찾을 수 없습니다." + exit 1 + fi + + # VMID 입력 + echo "" + read -p "복구할 원본 VMID: " SOURCE_VMID + + # 백업 목록 표시 + echo "" + if ! list_backups_by_vmid "$PBS_STORAGE" "$SOURCE_VMID"; then + exit 1 + fi + + # 백업 선택 + read -p "복구할 백업 번호를 선택하세요: " backup_num + + # 백업 목록 다시 생성 (서브셸 문제 해결) + local count=0 + declare -A BACKUP_LIST_LOCAL + while IFS= read -r line; do + ((count++)) + local volid=$(echo "$line" | awk '{print $1}') + BACKUP_LIST_LOCAL[$count]="$volid" + done < <(pvesm list "$PBS_STORAGE" 2>/dev/null | grep -E "vm-${SOURCE_VMID}-|ct-${SOURCE_VMID}-") + + SELECTED_VOLID="${BACKUP_LIST_LOCAL[$backup_num]}" + + if [[ -z "$SELECTED_VOLID" ]]; then + log_error "잘못된 선택입니다." + exit 1 + fi + + # 타겟 VMID 입력 + echo "" + read -p "복구할 타겟 VMID (기본값: ${SOURCE_VMID}): " TARGET_VMID + TARGET_VMID=${TARGET_VMID:-$SOURCE_VMID} + + # VMID 사용 가능 여부 확인 + if ! check_vmid_available "$TARGET_VMID"; then + read -p "새로운 VMID를 입력하세요: " TARGET_VMID + if ! check_vmid_available "$TARGET_VMID"; then + exit 1 + fi + fi + + # 스토리지 선택 + echo "" + list_storages + read -p "복구할 스토리지 (Enter로 원본 스토리지 사용): " TARGET_STORAGE + + # VM 타입 판단 + echo "" + if [[ "$SELECTED_VOLID" =~ "vm-" ]]; then + log_info "VM 백업으로 감지되었습니다." + BACKUP_TYPE="vm" + elif [[ "$SELECTED_VOLID" =~ "ct-" ]]; then + log_info "Container 백업으로 감지되었습니다." + BACKUP_TYPE="ct" + else + read -p "백업 타입 (vm/ct): " BACKUP_TYPE + fi + + # 추가 옵션 + echo "" + log_info "추가 옵션 (선택사항)" + read -p "추가 옵션 (예: --force): " EXTRA_OPTIONS + + # 복구 실행 + echo "" + if [[ "$BACKUP_TYPE" == "vm" ]]; then + restore_vm "$SELECTED_VOLID" "$TARGET_VMID" "$TARGET_STORAGE" "$EXTRA_OPTIONS" + else + restore_container "$SELECTED_VOLID" "$TARGET_VMID" "$TARGET_STORAGE" "$EXTRA_OPTIONS" + fi +} + +# 사용법 +usage() { + cat << EOF +사용법: $0 [옵션] + +대화형 모드 (권장): + $0 + +리스트 모드: + $0 -l -s PBS_STORAGE [-v VMID] + +복구 모드: + $0 -r -V VOLID -t TARGET_VMID [-S STORAGE] [-T TYPE] + +옵션: + -l 백업 목록 보기 + -r 백업 복구 + -s STORAGE PBS 스토리지 이름 + -v VMID 검색할 VMID + -V VOLID 복구할 Volume ID + -t VMID 타겟 VMID + -S STORAGE 복구할 스토리지 + -T TYPE 백업 타입 (vm 또는 ct) + -o OPTIONS 추가 옵션 + -h 도움말 표시 + +예시: + # 대화형 모드 + $0 + + # PBS 스토리지의 모든 백업 보기 + $0 -l -s pbs-backup + + # 특정 VMID의 백업 보기 + $0 -l -s pbs-backup -v 100 + + # VM 백업 복구 + $0 -r -V "pbs-backup:backup/vm-100-2025_01_28-10_00_00.vma.zst" \\ + -t 100 -T vm + + # Container 복구 (다른 VMID로) + $0 -r -V "pbs-backup:backup/ct-200-2025_01_28-10_00_00.tar.zst" \\ + -t 201 -S local-lvm -T ct + +EOF + exit 0 +} + +# 메인 함수 +main() { + print_banner + check_root + check_proxmox + + # 옵션 없으면 대화형 모드 + if [[ $# -eq 0 ]]; then + interactive_mode + exit 0 + fi + + local MODE="" + local PBS_STORAGE="" + local VMID="" + local VOLID="" + local TARGET_VMID="" + local TARGET_STORAGE="" + local BACKUP_TYPE="" + local EXTRA_OPTIONS="" + + # 옵션 파싱 + while getopts "lrs:v:V:t:S:T:o:h" opt; do + case $opt in + l) MODE="list" ;; + r) MODE="restore" ;; + s) PBS_STORAGE="$OPTARG" ;; + v) VMID="$OPTARG" ;; + V) VOLID="$OPTARG" ;; + t) TARGET_VMID="$OPTARG" ;; + S) TARGET_STORAGE="$OPTARG" ;; + T) BACKUP_TYPE="$OPTARG" ;; + o) EXTRA_OPTIONS="$OPTARG" ;; + h) usage ;; + *) usage ;; + esac + done + + # 모드별 실행 + case "$MODE" in + list) + if [[ -z "$PBS_STORAGE" ]]; then + log_error "PBS 스토리지를 지정하세요 (-s 옵션)" + exit 1 + fi + + if [[ -n "$VMID" ]]; then + list_backups_by_vmid "$PBS_STORAGE" "$VMID" + else + list_backups "$PBS_STORAGE" + fi + ;; + + restore) + if [[ -z "$VOLID" ]] || [[ -z "$TARGET_VMID" ]]; then + log_error "Volume ID(-V)와 타겟 VMID(-t)를 지정하세요" + exit 1 + fi + + if ! check_vmid_available "$TARGET_VMID"; then + exit 1 + fi + + # 타입 자동 감지 + if [[ -z "$BACKUP_TYPE" ]]; then + if [[ "$VOLID" =~ "vm-" ]]; then + BACKUP_TYPE="vm" + elif [[ "$VOLID" =~ "ct-" ]]; then + BACKUP_TYPE="ct" + else + log_error "백업 타입을 지정하세요 (-T vm 또는 -T ct)" + exit 1 + fi + fi + + if [[ "$BACKUP_TYPE" == "vm" ]]; then + restore_vm "$VOLID" "$TARGET_VMID" "$TARGET_STORAGE" "$EXTRA_OPTIONS" + else + restore_container "$VOLID" "$TARGET_VMID" "$TARGET_STORAGE" "$EXTRA_OPTIONS" + fi + ;; + + *) + usage + ;; + esac +} + +# 스크립트 실행 +main "$@"