모든 PBS API 호출에 ns=PQ 파라미터를 추가하여 PQ 네임스페이스의 백업만 조회하도록 수정 - groups API에 ns=PQ 추가 - snapshots API에 ns=PQ 추가 (get_snapshot_comment 함수) - snapshots API에 ns=PQ 추가 (get_latest_snapshot 함수) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
630 lines
20 KiB
Bash
630 lines
20 KiB
Bash
#!/bin/bash
|
|
#
|
|
# PBS 올인원 스크립트 - Proxmox 호스트에서 실행
|
|
# 작성일: 2025-11-04
|
|
# 용도: PBS 등록 → 백업 조회 → 복구를 한 번에
|
|
#
|
|
|
|
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"
|
|
|
|
# 복구 설정
|
|
TEMPLATE_VMID=""
|
|
TARGET_VMID=""
|
|
TARGET_STORAGE="local-lvm"
|
|
BACKUP_TYPE=""
|
|
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 올인원 스크립트 ║
|
|
║ 등록 → 조회 → 복구 한 번에! ║
|
|
║ ║
|
|
╚═══════════════════════════════════════════════════════════════╝
|
|
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 스토리지가 이미 등록되어 있습니다."
|
|
read -p "$(echo -e ${YELLOW}재등록하시겠습니까?${NC}) (y/N): " reregister < /dev/tty
|
|
|
|
if [[ "$reregister" =~ ^[Yy]$ ]]; then
|
|
log_info "기존 PBS 스토리지 제거 중..."
|
|
pvesm remove "${PBS_STORAGE_NAME}"
|
|
sleep 1
|
|
else
|
|
log_info "기존 PBS 스토리지 사용"
|
|
return 0
|
|
fi
|
|
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 인증 완료"
|
|
}
|
|
|
|
# 백업 그룹 목록 조회 및 선택
|
|
list_and_select_backup() {
|
|
log_step "PBS에서 백업 목록 조회 중..."
|
|
echo ""
|
|
|
|
# 응답을 파일에 저장 (PQ 네임스페이스 명시)
|
|
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
|
|
|
|
# 디버깅: 응답 확인
|
|
if [ ! -s /tmp/pbs_groups.json ]; then
|
|
log_error "PBS API 응답이 비어있습니다"
|
|
log_info "PBS_TICKET: ${PBS_TICKET:0:50}..."
|
|
log_info "PBS_CSRF: ${PBS_CSRF:0:50}..."
|
|
return 1
|
|
fi
|
|
|
|
# 디버깅: 응답 길이 확인
|
|
log_info "API 응답 수신 ($(stat -c%s /tmp/pbs_groups.json) bytes)"
|
|
|
|
# PBS 인증 정보를 환경 변수로 export
|
|
export PBS_TICKET
|
|
export PBS_CSRF
|
|
|
|
python3 << PYEOF
|
|
import sys, json, subprocess, os
|
|
|
|
# PBS 인증 정보 가져오기
|
|
PBS_TICKET = os.environ.get('PBS_TICKET', '')
|
|
PBS_CSRF = os.environ.get('PBS_CSRF', '')
|
|
PBS_SERVER = "${PBS_SERVER}"
|
|
PBS_PORT = "${PBS_PORT}"
|
|
PBS_DATASTORE = "${PBS_DATASTORE}"
|
|
|
|
def get_snapshot_comment(backup_type, backup_id):
|
|
"""최신 스냅샷의 comment를 가져오기"""
|
|
try:
|
|
cmd = [
|
|
'curl', '-k', '-s', '-X', 'GET',
|
|
f'https://{PBS_SERVER}:{PBS_PORT}/api2/json/admin/datastore/{PBS_DATASTORE}/snapshots?backup-type={backup_type}&backup-id={backup_id}&ns=PQ',
|
|
'-H', f'Cookie: PBSAuthCookie={PBS_TICKET}',
|
|
'-H', f'CSRFPreventionToken: {PBS_CSRF}'
|
|
]
|
|
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
data = json.loads(result.stdout)
|
|
snapshots = data.get("data", [])
|
|
|
|
if snapshots:
|
|
# 가장 최근 스냅샷 찾기
|
|
latest = max(snapshots, key=lambda x: x.get("backup-time", 0))
|
|
return latest.get("comment", "")
|
|
except:
|
|
pass
|
|
return ""
|
|
|
|
try:
|
|
with open('/tmp/pbs_groups.json', 'r') as f:
|
|
data = json.load(f)
|
|
groups = data.get("data", [])
|
|
|
|
vm_groups = [g for g in groups if g.get("backup-type") == "vm"]
|
|
ct_groups = [g for g in groups if g.get("backup-type") == "ct"]
|
|
|
|
if vm_groups:
|
|
print("\033[1;36m=== VM 백업 ===\033[0m")
|
|
for i, group in enumerate(vm_groups, 1):
|
|
backup_id = group.get("backup-id", "N/A")
|
|
backup_count = group.get("backup-count", 0)
|
|
last_backup = group.get("last-backup", 0)
|
|
|
|
# groups에서 comment 가져오기 (없으면 스냅샷에서 조회)
|
|
comment = group.get("comment", "")
|
|
if not comment:
|
|
comment = get_snapshot_comment("vm", backup_id)
|
|
|
|
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"
|
|
|
|
if comment:
|
|
print(f" {i}. VM {backup_id:<6} - {backup_count}개 백업 (최근: {last_str})")
|
|
print(f" \033[0;90m└─ {comment}\033[0m")
|
|
else:
|
|
print(f" {i}. VM {backup_id:<6} - {backup_count}개 백업 (최근: {last_str})")
|
|
|
|
if ct_groups:
|
|
print("\n\033[1;36m=== CT 백업 ===\033[0m")
|
|
for i, group in enumerate(ct_groups, 1):
|
|
backup_id = group.get("backup-id", "N/A")
|
|
backup_count = group.get("backup-count", 0)
|
|
last_backup = group.get("last-backup", 0)
|
|
|
|
# groups에서 comment 가져오기 (없으면 스냅샷에서 조회)
|
|
comment = group.get("comment", "")
|
|
if not comment:
|
|
comment = get_snapshot_comment("ct", backup_id)
|
|
|
|
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"
|
|
|
|
if comment:
|
|
print(f" {i}. CT {backup_id:<6} - {backup_count}개 백업 (최근: {last_str})")
|
|
print(f" \033[0;90m└─ {comment}\033[0m")
|
|
else:
|
|
print(f" {i}. CT {backup_id:<6} - {backup_count}개 백업 (최근: {last_str})")
|
|
|
|
print("")
|
|
|
|
except Exception as e:
|
|
print(f"오류: {e}")
|
|
sys.exit(1)
|
|
PYEOF
|
|
|
|
# 백업 선택
|
|
echo ""
|
|
while true; do
|
|
read -p "$(echo -e ${CYAN}백업 타입${NC}) (vm/ct): " BACKUP_TYPE < /dev/tty
|
|
if [[ "$BACKUP_TYPE" =~ ^(vm|ct)$ ]]; then
|
|
break
|
|
fi
|
|
log_error "vm 또는 ct를 입력하세요"
|
|
done
|
|
|
|
read -p "$(echo -e ${CYAN}백업 ID${NC}): " TEMPLATE_VMID < /dev/tty
|
|
if [ -z "$TEMPLATE_VMID" ]; then
|
|
log_error "백업 ID는 필수입니다"
|
|
exit 1
|
|
fi
|
|
|
|
# 최신 스냅샷 찾기
|
|
log_info "최신 백업 조회 중..."
|
|
if get_latest_snapshot "$BACKUP_TYPE" "$TEMPLATE_VMID"; then
|
|
log_success "최신 백업: ${LATEST_SNAPSHOT}"
|
|
else
|
|
log_error "${BACKUP_TYPE} ${TEMPLATE_VMID}의 백업을 찾을 수 없습니다"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# 최신 스냅샷 조회
|
|
get_latest_snapshot() {
|
|
local backup_type="$1"
|
|
local backup_id="$2"
|
|
|
|
curl -k -s -X GET "https://${PBS_SERVER}:${PBS_PORT}/api2/json/admin/datastore/${PBS_DATASTORE}/snapshots?backup-type=${backup_type}&backup-id=${backup_id}&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
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# 사용 가능한 스토리지 목록 표시
|
|
list_available_storage() {
|
|
echo ""
|
|
log_step "사용 가능한 스토리지 목록"
|
|
echo ""
|
|
|
|
# VM/CT 이미지를 저장할 수 있는 스토리지만 필터링
|
|
pvesm status -content images,rootdir 2>/dev/null | tail -n +2 | while read -r line; do
|
|
storage_name=$(echo "$line" | awk '{print $1}')
|
|
storage_type=$(echo "$line" | awk '{print $2}')
|
|
total_mb=$(echo "$line" | awk '{print $4}')
|
|
used_mb=$(echo "$line" | awk '{print $5}')
|
|
avail_mb=$(echo "$line" | awk '{print $6}')
|
|
usage_pct=$(echo "$line" | awk '{print $7}')
|
|
|
|
# MB를 GB로 변환
|
|
total_gb=$(echo "scale=2; $total_mb / 1024 / 1024" | bc)
|
|
avail_gb=$(echo "scale=2; $avail_mb / 1024 / 1024" | bc)
|
|
|
|
echo -e " ${GREEN}●${NC} ${CYAN}${storage_name}${NC}"
|
|
echo -e " 타입: ${storage_type} | 용량: ${total_gb}GB | 여유: ${avail_gb}GB | 사용률: ${usage_pct}"
|
|
done
|
|
|
|
echo ""
|
|
}
|
|
|
|
# 복구 설정 입력
|
|
get_restore_config() {
|
|
echo ""
|
|
log_step "복구 설정을 입력하세요"
|
|
echo ""
|
|
|
|
# 복구할 VM/CT ID
|
|
read -p "$(echo -e ${CYAN}복구할 VM/CT ID${NC}) [기본값: ${TEMPLATE_VMID}]: " TARGET_VMID < /dev/tty
|
|
TARGET_VMID=${TARGET_VMID:-$TEMPLATE_VMID}
|
|
|
|
# 사용 가능한 스토리지 목록 표시
|
|
list_available_storage
|
|
|
|
# 스토리지 선택
|
|
while true; do
|
|
read -p "$(echo -e ${CYAN}저장 스토리지 이름${NC}) [기본값: local-lvm]: " input_storage < /dev/tty
|
|
TARGET_STORAGE=${input_storage:-local-lvm}
|
|
|
|
# 입력한 스토리지가 존재하는지 확인
|
|
if pvesm status -storage "${TARGET_STORAGE}" &>/dev/null; then
|
|
# VM/CT 이미지를 저장할 수 있는지 확인
|
|
if pvesm status -storage "${TARGET_STORAGE}" -content images &>/dev/null || \
|
|
pvesm status -storage "${TARGET_STORAGE}" -content rootdir &>/dev/null; then
|
|
log_success "스토리지 '${TARGET_STORAGE}' 선택됨"
|
|
break
|
|
else
|
|
log_error "스토리지 '${TARGET_STORAGE}'는 VM/CT 이미지를 저장할 수 없습니다"
|
|
fi
|
|
else
|
|
log_error "스토리지 '${TARGET_STORAGE}'를 찾을 수 없습니다"
|
|
fi
|
|
echo ""
|
|
done
|
|
|
|
echo ""
|
|
log_info "복구 요약:"
|
|
echo " 백업 소스: ${BACKUP_TYPE}/${TEMPLATE_VMID}"
|
|
echo " 백업 시점: ${LATEST_SNAPSHOT}"
|
|
echo " 복구 ID: ${TARGET_VMID}"
|
|
echo " 저장 위치: ${TARGET_STORAGE}"
|
|
echo ""
|
|
|
|
read -p "$(echo -e ${YELLOW}이 설정으로 복구하시겠습니까?${NC}) (y/N): " confirm < /dev/tty
|
|
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
|
|
log_info "작업 취소됨"
|
|
exit 0
|
|
fi
|
|
}
|
|
|
|
# VM/CT 복구
|
|
restore_backup() {
|
|
log_step "백업 복구 중... (시간이 걸릴 수 있습니다)"
|
|
echo ""
|
|
|
|
# 백업 경로 구성
|
|
BACKUP_PATH="${PBS_STORAGE_NAME}:backup/${BACKUP_TYPE}/${TEMPLATE_VMID}/${LATEST_SNAPSHOT}"
|
|
|
|
log_info "백업 경로: ${BACKUP_PATH}"
|
|
|
|
# 기존 VM/CT 확인
|
|
if qm status ${TARGET_VMID} 2>/dev/null || pct status ${TARGET_VMID} 2>/dev/null; then
|
|
log_warning "VM/CT ${TARGET_VMID}가 이미 존재합니다"
|
|
read -p "$(echo -e ${YELLOW}삭제하고 복구하시겠습니까?${NC}) (y/N): " delete_confirm < /dev/tty
|
|
|
|
if [[ "$delete_confirm" =~ ^[Yy]$ ]]; then
|
|
log_info "기존 VM/CT 삭제 중..."
|
|
if [ "$BACKUP_TYPE" = "vm" ]; then
|
|
qm stop ${TARGET_VMID} --skiplock || true
|
|
sleep 2
|
|
qm destroy ${TARGET_VMID} --purge --skiplock || true
|
|
else
|
|
pct stop ${TARGET_VMID} || true
|
|
sleep 2
|
|
pct destroy ${TARGET_VMID} --purge || true
|
|
fi
|
|
sleep 2
|
|
log_success "기존 VM/CT 삭제 완료"
|
|
else
|
|
log_error "복구 취소됨"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# 복구 실행
|
|
if [ "$BACKUP_TYPE" = "vm" ]; then
|
|
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 복구 완료!"
|
|
else
|
|
echo ""
|
|
log_error "VM 복구 실패. 로그: /tmp/pbs-restore.log"
|
|
return 1
|
|
fi
|
|
else
|
|
log_info "CT 복구 시작..."
|
|
echo ""
|
|
|
|
pct restore ${TARGET_VMID} "${BACKUP_PATH}" \
|
|
--storage ${TARGET_STORAGE} \
|
|
--unprivileged 1 2>&1 | tee /tmp/pbs-restore.log
|
|
|
|
if [ ${PIPESTATUS[0]} -eq 0 ]; then
|
|
echo ""
|
|
log_success "CT 복구 완료!"
|
|
else
|
|
echo ""
|
|
log_error "CT 복구 실패. 로그: /tmp/pbs-restore.log"
|
|
return 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# VM/CT 시작
|
|
start_vm() {
|
|
echo ""
|
|
read -p "$(echo -e ${YELLOW}VM/CT를 바로 시작하시겠습니까?${NC}) (y/N): " start_confirm < /dev/tty
|
|
|
|
if [[ "$start_confirm" =~ ^[Yy]$ ]]; then
|
|
log_info "VM/CT 시작 중..."
|
|
|
|
if [ "$BACKUP_TYPE" = "vm" ]; then
|
|
qm start ${TARGET_VMID}
|
|
else
|
|
pct start ${TARGET_VMID}
|
|
fi
|
|
|
|
if [ $? -eq 0 ]; then
|
|
log_success "VM/CT 시작 완료!"
|
|
|
|
sleep 3
|
|
log_info "상태 확인 중..."
|
|
if [ "$BACKUP_TYPE" = "vm" ]; then
|
|
qm status ${TARGET_VMID}
|
|
else
|
|
pct status ${TARGET_VMID}
|
|
fi
|
|
else
|
|
log_error "시작 실패"
|
|
return 1
|
|
fi
|
|
else
|
|
log_info "수동으로 시작하세요:"
|
|
if [ "$BACKUP_TYPE" = "vm" ]; then
|
|
echo " qm start ${TARGET_VMID}"
|
|
else
|
|
echo " pct start ${TARGET_VMID}"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# 완료 메시지
|
|
print_summary() {
|
|
echo ""
|
|
echo -e "${GREEN}"
|
|
cat << "EOF"
|
|
╔═══════════════════════════════════════════════════════════════╗
|
|
║ ║
|
|
║ ✅ 복구 완료! ║
|
|
║ ║
|
|
╚═══════════════════════════════════════════════════════════════╝
|
|
EOF
|
|
echo -e "${NC}"
|
|
echo ""
|
|
|
|
log_info "복구 정보:"
|
|
echo " 백업 소스: ${BACKUP_TYPE}/${TEMPLATE_VMID}"
|
|
echo " 백업 시점: ${LATEST_SNAPSHOT}"
|
|
echo " 복구 ID: ${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 ""
|
|
|
|
if [ "$BACKUP_TYPE" = "vm" ]; then
|
|
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}"
|
|
else
|
|
log_info "유용한 명령어:"
|
|
echo " CT 상태: pct status ${TARGET_VMID}"
|
|
echo " CT 시작: pct start ${TARGET_VMID}"
|
|
echo " CT 중지: pct stop ${TARGET_VMID}"
|
|
echo " CT 접속: pct enter ${TARGET_VMID}"
|
|
fi
|
|
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 ""
|
|
|
|
# 백업 목록 조회 및 선택
|
|
list_and_select_backup
|
|
echo ""
|
|
|
|
# 복구 설정 입력
|
|
get_restore_config
|
|
echo ""
|
|
|
|
# 백업 복구
|
|
restore_backup || exit 1
|
|
echo ""
|
|
|
|
# VM/CT 시작
|
|
start_vm
|
|
echo ""
|
|
|
|
# 완료 메시지
|
|
print_summary
|
|
}
|
|
|
|
# 도움말
|
|
if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "PBS 올인원 스크립트 - Proxmox 호스트에서 실행"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo ""
|
|
echo "사용법: $0"
|
|
echo ""
|
|
echo "이 스크립트는:"
|
|
echo " 1. PBS 스토리지를 자동 등록"
|
|
echo " 2. API로 백업 목록 조회"
|
|
echo " 3. 선택한 백업을 복구"
|
|
echo ""
|
|
echo "실행 예시:"
|
|
echo " bash $0"
|
|
echo ""
|
|
echo "사전 준비:"
|
|
echo " - Proxmox VE 설치 완료"
|
|
echo " - PBS 서버 (100.64.0.10) 접근 가능"
|
|
echo " - python3 설치 (보통 기본 설치됨)"
|
|
echo ""
|
|
echo "모든 설정(IP, 비밀번호 등)은 자동 입력됩니다!"
|
|
echo ""
|
|
exit 0
|
|
fi
|
|
|
|
# Root 권한 확인
|
|
if [ "$EUID" -ne 0 ]; then
|
|
log_error "이 스크립트는 root 권한이 필요합니다."
|
|
log_info "다음 명령으로 실행하세요: sudo $0"
|
|
exit 1
|
|
fi
|
|
|
|
# 스크립트 실행
|
|
main
|