This commit is contained in:
unknown
2025-12-13 08:12:44 +09:00
parent b931bd9eb6
commit c20bd6a067
6 changed files with 260 additions and 59 deletions

View File

@@ -1,8 +1,12 @@
import httpx
import asyncio
import logging
from typing import List, Optional, Dict
from app.config import settings
import json
logger = logging.getLogger(__name__)
class ProxmoxService:
"""Proxmox VE API 통신 서비스"""
@@ -16,10 +20,20 @@ class ProxmoxService:
url = f"{self.base_url}/api2/json{endpoint}"
headers = {"Authorization": self.api_token}
async with httpx.AsyncClient(verify=self.verify_ssl, timeout=30.0) as client:
response = await client.request(method, url, headers=headers, **kwargs)
response.raise_for_status()
return response.json()
try:
async with httpx.AsyncClient(verify=self.verify_ssl, timeout=30.0) as client:
response = await client.request(method, url, headers=headers, **kwargs)
response.raise_for_status()
return response.json()
except httpx.HTTPStatusError as e:
logger.error(f"Proxmox API HTTP Error: {e.response.status_code} - {e.response.text}")
raise
except httpx.RequestError as e:
logger.error(f"Proxmox API Request Error: {e}")
raise
except Exception as e:
logger.error(f"Proxmox API Unexpected Error: {e}")
raise
async def get_nodes(self) -> List[Dict]:
"""노드 목록 조회"""
@@ -32,32 +46,33 @@ class ProxmoxService:
return result.get("data", [])
async def get_all_vms(self) -> List[Dict]:
"""모든 노드의 VM 목록 조회"""
nodes = await self.get_nodes()
print(f"DEBUG: 노드 목록: {nodes}")
all_vms = []
for node in nodes:
node_name = node.get("node")
if node_name:
vms = await self.get_vms(node_name)
print(f"DEBUG: {node_name} 노드의 VM 목록: {vms}")
for vm in vms:
vm["node"] = node_name
all_vms.append(vm)
print(f"DEBUG: 전체 VM 개수: {len(all_vms)}")
print(f"DEBUG: 전체 VM 목록: {all_vms}")
return all_vms
"""모든 노드의 VM 및 LXC 목록 조회 (클러스터 리소스 API 사용)"""
try:
# /cluster/resources 호출 (type=vm은 qemu와 lxc 모두 포함)
result = await self._make_request("GET", "/cluster/resources?type=vm")
resources = result.get("data", [])
logger.info(f"Total resources fetched: {len(resources)}")
return resources
except Exception as e:
logger.error(f"Failed to get cluster resources: {e}")
return []
async def get_vm_status(self, node: str, vm_id: int) -> Dict:
"""VM 상태 조회"""
result = await self._make_request("GET", f"/nodes/{node}/qemu/{vm_id}/status/current")
return result.get("data", {})
async def get_vm_status(self, node: str, vm_id: int, vm_type: str = "qemu") -> Dict:
"""VM/LXC 상태 조회"""
try:
result = await self._make_request("GET", f"/nodes/{node}/{vm_type}/{vm_id}/status/current")
return result.get("data", {})
except Exception as e:
logger.error(f"Failed to get status for {vm_type} {vm_id}: {e}")
return {}
async def get_vm_ip(self, node: str, vm_id: int) -> Optional[str]:
"""QEMU Guest Agent를 통해 VM IP 주소 조회"""
async def get_vm_ip(self, node: str, vm_id: int, vm_type: str = "qemu") -> Optional[str]:
"""QEMU Guest Agent를 통해 VM IP 주소 조회 (LXC는 미지원)"""
if vm_type != "qemu":
return None
try:
result = await self._make_request(
"GET",
@@ -80,31 +95,38 @@ class ProxmoxService:
return address
return None
except:
except Exception as e:
logger.warning(f"Failed to get VM IP (Node: {node}, VMID: {vm_id}): {e}")
return None
async def start_vm(self, node: str, vm_id: int) -> bool:
"""VM 시작"""
async def start_vm(self, node: str, vm_id: int, vm_type: str = "qemu") -> bool:
"""VM/LXC 시작"""
try:
await self._make_request("POST", f"/nodes/{node}/qemu/{vm_id}/status/start")
await self._make_request("POST", f"/nodes/{node}/{vm_type}/{vm_id}/status/start")
logger.info(f"{vm_type} started: {vm_id} (Node: {node})")
return True
except:
except Exception as e:
logger.error(f"Failed to start {vm_type} {vm_id}: {e}")
return False
async def stop_vm(self, node: str, vm_id: int) -> bool:
"""VM 종료"""
async def stop_vm(self, node: str, vm_id: int, vm_type: str = "qemu") -> bool:
"""VM/LXC 종료"""
try:
await self._make_request("POST", f"/nodes/{node}/qemu/{vm_id}/status/stop")
await self._make_request("POST", f"/nodes/{node}/{vm_type}/{vm_id}/status/stop")
logger.info(f"{vm_type} stopped: {vm_id} (Node: {node})")
return True
except:
except Exception as e:
logger.error(f"Failed to stop {vm_type} {vm_id}: {e}")
return False
async def reboot_vm(self, node: str, vm_id: int) -> bool:
"""VM 재시작"""
async def reboot_vm(self, node: str, vm_id: int, vm_type: str = "qemu") -> bool:
"""VM/LXC 재시작"""
try:
await self._make_request("POST", f"/nodes/{node}/qemu/{vm_id}/status/reboot")
await self._make_request("POST", f"/nodes/{node}/{vm_type}/{vm_id}/status/reboot")
logger.info(f"{vm_type} rebooted: {vm_id} (Node: {node})")
return True
except:
except Exception as e:
logger.error(f"Failed to reboot {vm_type} {vm_id}: {e}")
return False
def _is_private_ip(self, ip: str) -> bool: