128 lines
4.2 KiB
Python
128 lines
4.2 KiB
Python
from fastapi import APIRouter, Depends
|
|
from sqlalchemy.orm import Session
|
|
from app.database import get_db
|
|
from app.schemas.tunnel import (
|
|
TunnelCreateRequest,
|
|
TunnelCreateResponse,
|
|
TunnelStatusResponse,
|
|
TunnelCloseResponse,
|
|
TunnelInfo
|
|
)
|
|
from app.schemas.auth import CurrentUser
|
|
from app.api.auth import get_current_user
|
|
from app.services.ssh_tunnel_service import ssh_tunnel_manager
|
|
from app.services.proxmox_service import proxmox_service
|
|
from app.models.vm import VMAccess
|
|
from app.utils.exceptions import NotFoundError, BadRequestError
|
|
|
|
router = APIRouter()
|
|
|
|
@router.post("/create", response_model=TunnelCreateResponse)
|
|
async def create_tunnel(
|
|
request: TunnelCreateRequest,
|
|
current_user: CurrentUser = Depends(get_current_user),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""
|
|
SSH 터널 생성
|
|
|
|
VM에 접속하기 위한 SSH Local Port Forwarding 터널을 생성합니다.
|
|
클라이언트는 반환된 local_port로 RDP 연결을 시도해야 합니다.
|
|
"""
|
|
# VM IP 주소 확인
|
|
# 1. 요청에 IP가 포함되어 있으면 사용 (Guest Agent 없이도 가능)
|
|
ip_address = request.vm_ip
|
|
|
|
# 2. 없으면 Guest Agent로 조회
|
|
if not ip_address:
|
|
try:
|
|
ip_address = await proxmox_service.get_vm_ip(request.node, request.vm_id)
|
|
if ip_address:
|
|
print(f"✅ Guest Agent에서 IP 조회 성공: {ip_address}")
|
|
except Exception as e:
|
|
print(f"⚠️ Guest Agent IP 조회 실패: {str(e)}")
|
|
ip_address = None
|
|
|
|
# 3. Guest Agent 실패 시 VMAccess 테이블의 static_ip 사용
|
|
if not ip_address:
|
|
vm_access = db.query(VMAccess).filter(
|
|
VMAccess.user_id == current_user.id,
|
|
VMAccess.vm_id == request.vm_id,
|
|
VMAccess.node == request.node
|
|
).first()
|
|
|
|
if vm_access and vm_access.static_ip:
|
|
ip_address = vm_access.static_ip
|
|
print(f"✅ Static IP 사용: {ip_address}")
|
|
else:
|
|
raise BadRequestError(
|
|
"VM의 IP 주소를 확인할 수 없습니다. "
|
|
"관리자 패널에서 고정 IP를 설정하거나 QEMU Guest Agent를 설치하세요."
|
|
)
|
|
|
|
try:
|
|
# SSH 터널 생성
|
|
tunnel_info = await ssh_tunnel_manager.create_tunnel(
|
|
user_id=current_user.id,
|
|
vm_id=request.vm_id,
|
|
remote_host=ip_address,
|
|
remote_port=3389 # RDP 포트
|
|
)
|
|
|
|
return TunnelCreateResponse(
|
|
success=True,
|
|
message="SSH 터널이 생성되었습니다",
|
|
session_id=tunnel_info["session_id"],
|
|
tunnel_info=TunnelInfo(
|
|
session_id=tunnel_info["session_id"],
|
|
local_port=tunnel_info["local_port"],
|
|
remote_host=tunnel_info["remote_host"],
|
|
remote_port=tunnel_info["remote_port"],
|
|
vm_id=request.vm_id,
|
|
is_active=True,
|
|
created_at=None # 자동 설정
|
|
)
|
|
)
|
|
except Exception as e:
|
|
return TunnelCreateResponse(
|
|
success=False,
|
|
message=f"터널 생성 실패: {str(e)}",
|
|
session_id="",
|
|
tunnel_info=None
|
|
)
|
|
|
|
@router.get("/{session_id}/status", response_model=TunnelStatusResponse)
|
|
async def get_tunnel_status(
|
|
session_id: str,
|
|
current_user: CurrentUser = Depends(get_current_user)
|
|
):
|
|
"""
|
|
터널 상태 조회
|
|
|
|
활성 터널의 상태를 확인합니다.
|
|
"""
|
|
status = await ssh_tunnel_manager.get_tunnel_status(session_id)
|
|
|
|
if not status:
|
|
raise NotFoundError("터널을 찾을 수 없습니다")
|
|
|
|
return TunnelStatusResponse(**status)
|
|
|
|
@router.delete("/{session_id}", response_model=TunnelCloseResponse)
|
|
async def close_tunnel(
|
|
session_id: str,
|
|
current_user: CurrentUser = Depends(get_current_user)
|
|
):
|
|
"""
|
|
터널 종료
|
|
|
|
SSH 터널을 종료하고 리소스를 정리합니다.
|
|
"""
|
|
success = await ssh_tunnel_manager.close_tunnel(session_id)
|
|
|
|
return TunnelCloseResponse(
|
|
success=success,
|
|
message="터널이 종료되었습니다" if success else "터널 종료에 실패했습니다",
|
|
session_id=session_id
|
|
)
|