---
name: dr-automation
description: "GUARDiA DR(재해복구) 자동화 구현 스킬. DR 시나리오 관리, Failover 실행, 백업 무결성 검증, 복구 테스트, RTO/RPO 추적 기능을 FastAPI + paramiko 패턴으로 구현한다. 다음 상황에서 반드시 사용: (1) 'DR 구현', '재해복구', 'Failover', 'RTO/RPO' 관련 요청; (2) dr.py 라우터 또는 core/dr_engine.py 작업; (3) 백업 무결성 검증, 복구 테스트 구현; (4) DR 대시보드 구현; (5) 다시 실행, 업데이트, 보완 요청. paramiko SSH 패턴과 SQLAlchemy async 패턴을 따른다."
---

# DR 자동화 구현 스킬

## 구현 대상 파일
- `itsm/core/dr_engine.py` — DR 비즈니스 로직
- `itsm/routers/dr.py` — FastAPI 라우터

## 핵심 구현 원칙
1. **Fail-Safe 시퀀스**: 스냅샷 → 대기서버 활성화 → 서비스 전환 → 헬스체크 → 롤백(실패 시)
2. **자격증명 보호**: paramiko 접속 시 IP/계정 노출 금지, AES 복호화 후 메모리만 사용
3. **비동기**: asyncio.create_subprocess_exec + paramiko를 run_in_executor로 래핑
4. **감사 기록**: 모든 DR 작업은 tb_audit_log에 기록

## DB 모델 (models.py에 추가)

```python
class DRScenario(Base):
    __tablename__ = "tb_dr_scenario"
    id               = Column(Integer, primary_key=True)
    name             = Column(String(100), nullable=False)
    scenario_type    = Column(String(30))   # SITE_FAILURE | SERVER_FAILURE | DATA_CORRUPTION
    primary_server_id = Column(Integer, ForeignKey("tb_server_info.id"))
    standby_server_id = Column(Integer, ForeignKey("tb_server_info.id"))
    rto_minutes      = Column(Integer)      # 목표 RTO (분)
    rpo_minutes      = Column(Integer)      # 목표 RPO (분)
    failover_steps   = Column(JSON)         # 페일오버 실행 단계 목록
    healthcheck_url  = Column(String(255))
    last_test_at     = Column(DateTime)
    last_test_result = Column(String(20))   # PASS | FAIL | PARTIAL
    is_active        = Column(Boolean, default=True)
    created_at       = Column(DateTime, default=func.now())

class DRTest(Base):
    __tablename__ = "tb_dr_test"
    id              = Column(Integer, primary_key=True)
    scenario_id     = Column(Integer, ForeignKey("tb_dr_scenario.id"))
    test_type       = Column(String(20))    # BACKUP_VERIFY | FAILOVER_SIM | RECOVERY
    status          = Column(String(20))    # RUNNING | PASS | FAIL | PARTIAL
    rto_actual      = Column(Integer)       # 실제 RTO (분)
    rpo_actual      = Column(Integer)       # 실제 RPO (분)
    result_detail   = Column(JSON)          # 단계별 결과
    started_at      = Column(DateTime, default=func.now())
    completed_at    = Column(DateTime)
    triggered_by    = Column(String(100))
```

## core/dr_engine.py 구현 패턴

```python
import asyncio, hashlib, time
from datetime import datetime
from typing import Optional
import paramiko
from sqlalchemy.ext.asyncio import AsyncSession

from core.ssh_exec import _get_server_credentials  # 기존 AES 복호화 함수 재사용
from models import DRScenario, DRTest, Server

class DREngine:
    async def verify_backup(self, db: AsyncSession, server_name: str) -> dict:
        """백업 파일 무결성 검증 (SHA-256 체크)."""
        # 1. 서버 정보 조회 (ip_addr, ssh_user, os_pw_enc)
        # 2. AES 복호화로 자격증명 획득
        # 3. paramiko SSH 접속
        # 4. backup_path 하위 최신 파일 SHA-256 계산
        # 5. 결과 반환 (파일명, 크기, 해시, 경로 미노출)
        ...

    async def run_recovery_test(self, db: AsyncSession, scenario_id: int,
                                 triggered_by: str) -> DRTest:
        """복구 테스트 실행."""
        # 1. 시나리오 조회
        # 2. DRTest 레코드 생성 (status=RUNNING)
        # 3. failover_steps 순서대로 SSH 명령 실행
        # 4. 각 단계 결과 result_detail에 누적
        # 5. healthcheck_url 응답 확인
        # 6. RTO 계산 (started_at ~ 헬스체크 성공)
        # 7. DRTest status 업데이트 (PASS/FAIL/PARTIAL)
        ...

    def calculate_rto_rpo(self, tests: list[DRTest]) -> dict:
        """최근 5회 테스트 기반 RTO/RPO 통계."""
        ...
```

## routers/dr.py 엔드포인트 구조

```
GET  /api/dr/scenarios              목록 (ENGINEER 이상)
POST /api/dr/scenarios              등록 (ADMIN 전용)
GET  /api/dr/scenarios/{id}         상세
PUT  /api/dr/scenarios/{id}         수정 (ADMIN 전용)
POST /api/dr/test                   복구 테스트 실행 (ENGINEER 이상)
GET  /api/dr/test/{id}              테스트 결과 조회
GET  /api/dr/tests                  테스트 이력 목록
POST /api/dr/backup-verify          백업 무결성 검증 (ENGINEER 이상)
POST /api/dr/failover/{scenario_id} Failover 실행 (ADMIN 전용, 승인 필요)
GET  /api/dr/rto-rpo                RTO/RPO 현황 대시보드
GET  /api/dr/dashboard              DR 전체 현황
```

## 보안 규칙
- Failover 실행은 `require_admin_role` 의존성 필수
- 백업 검증은 ENGINEER 이상 허용
- 서버 IP/경로를 API 응답 body에 포함하지 않는다
- SSH 자격증명은 `core/ssh_exec.py`의 기존 AES 복호화 함수 재사용

## 헬스체크 URL 검증 방법
```python
import httpx
async with httpx.AsyncClient(verify=False, timeout=10) as client:
    resp = await client.get(scenario.healthcheck_url)
    return resp.status_code == 200
```
