refactor: split proxmox.py (4676 lines) into package with 7 modules

- _helpers.py: shared helpers (check_vm_access, _get_proxmox_client, etc.)
- servers.py: HTML pages, server CRUD, resources, node status (16 routes)
- sdn.py: SDN zones/vnets/subnets (10 routes)
- snapshots.py: VM/container snapshots + archives (10 routes)
- vms.py: VM/LXC control, HA, monitoring, VNC, terminal, bulk ops (42 routes)
- tasks.py: Proxmox task management (6 routes)
- __init__.py: re-exports unified router (84 routes total, no public API change)

https://claude.ai/code/session_019rK2wvaWg5zXFwmAPPEB7R
This commit is contained in:
Claude
2026-03-07 17:35:17 +00:00
parent 7bd2dd5b8f
commit 6fb7406d27
7 changed files with 2156 additions and 2013 deletions

View File

@@ -0,0 +1,148 @@
from fastapi import APIRouter, Depends, Request, HTTPException, Query, Form, status, WebSocket, WebSocketDisconnect
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.templating import Jinja2Templates
from sqlalchemy.orm import Session
from sqlalchemy import func
from loguru import logger
from typing import List
import ssl
import asyncio
import httpx
import websockets
from ..db import get_db
from ..models import ProxmoxServer, VMInstance, User, IPAMAllocation, IPAMNetwork, VMSnapshotArchive
from ..schemas import ProxmoxServerCreate, ProxmoxServerUpdate, ProxmoxServerResponse
from ..proxmox_client import ProxmoxClient, get_proxmox_resources
from ..auth import get_current_user, PermissionChecker, require_permission, check_permission
from ..logging_service import LoggingService
from ..template_helpers import add_i18n_context
from ..ipam_service import IPAMService
from ..models import TaskQueue
from ..services.task_queue_service import TaskQueueService, process_task_queue
from ._helpers import (check_vm_access, require_vm_access, _get_proxmox_client,
get_next_vmid, archive_and_delete_snapshots,
save_vm_instance, get_vm_instance, soft_delete_vm_instance,
templates)
router = APIRouter()
@router.get("/api/{server_id}/task/{upid}/status")
def get_task_status(
server_id: int,
upid: str,
node: str,
db: Session = Depends(get_db),
current_user: User = Depends(PermissionChecker("vms.view"))
):
"""Получить статус задачи по UPID"""
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
if not server:
raise HTTPException(status_code=404, detail="Proxmox server not found")
try:
client = _get_proxmox_client(server)
if not client.is_connected():
raise HTTPException(status_code=503, detail="Failed to connect to Proxmox server")
status = client.get_task_status(node, upid)
return JSONResponse(content=status)
except HTTPException:
raise
except Exception as e:
logger.error(f"Error getting task status {upid}: {e}")
raise HTTPException(status_code=500, detail=str(e))
@router.get("/api/{server_id}/task/{upid}/log")
def get_task_log(
server_id: int,
upid: str,
node: str,
start: int = 0,
limit: int = 50,
db: Session = Depends(get_db),
current_user: User = Depends(PermissionChecker("vms.view"))
):
"""Получить лог задачи по UPID"""
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
if not server:
raise HTTPException(status_code=404, detail="Proxmox server not found")
try:
client = _get_proxmox_client(server)
if not client.is_connected():
raise HTTPException(status_code=503, detail="Failed to connect to Proxmox server")
logs = client.get_task_log(node, upid, start, limit)
return JSONResponse(content={"logs": logs})
except HTTPException:
raise
except Exception as e:
logger.error(f"Error getting task log {upid}: {e}")
raise HTTPException(status_code=500, detail=str(e))
@router.get("/api/tasks")
def get_user_tasks(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
limit: int = Query(20, ge=1, le=100)
):
"""Get recent tasks for current user"""
tasks = TaskQueueService.get_user_tasks(db, current_user.id, limit)
return {
"tasks": [task.to_dict() for task in tasks]
}
@router.get("/api/tasks/active")
def get_active_tasks(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""Get all active (pending/running) tasks for current user"""
tasks = db.query(TaskQueue).filter(
TaskQueue.user_id == current_user.id,
TaskQueue.status.in_(['pending', 'running'])
).order_by(TaskQueue.created_at.desc()).all()
return {
"tasks": [task.to_dict() for task in tasks]
}
@router.get("/api/tasks/{task_id}")
def get_task_by_id(
task_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""Get status of a specific task"""
task = db.query(TaskQueue).filter(
TaskQueue.id == task_id,
TaskQueue.user_id == current_user.id
).first()
if not task:
raise HTTPException(status_code=404, detail="Task not found")
return task.to_dict()
@router.post("/api/tasks/{task_id}/cancel")
def cancel_task(
task_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""Cancel a pending task"""
success = TaskQueueService.cancel_task(db, task_id, current_user.id)
if not success:
raise HTTPException(status_code=400, detail="Task cannot be cancelled (not found, not pending, or not yours)")
return {"success": True, "message": "Task cancelled"}