123 lines
4.4 KiB
Python
123 lines
4.4 KiB
Python
import logging
|
||
from typing import List
|
||
|
||
from fastapi import APIRouter, Depends
|
||
from sqlalchemy.orm import Session
|
||
|
||
from .. import models, schemas, proxmox_client as pve
|
||
from ..database import get_db
|
||
from ..deps import get_current_user, require_admin
|
||
|
||
logger = logging.getLogger(__name__)
|
||
router = APIRouter(prefix="/templates", tags=["templates"])
|
||
|
||
|
||
def _normalize_lxc_source(value: str | None) -> str | None:
|
||
"""Приводит источник LXC к корректному Proxmox volid.
|
||
|
||
Принимает как полный volid:
|
||
``local:vztmpl/debian-13-standard_13.6-1_amd64.tar.zst``
|
||
|
||
так и короткое имя:
|
||
``debian-13-standard_13.6-1_amd64``.
|
||
"""
|
||
if not value:
|
||
return None
|
||
|
||
source = value.strip()
|
||
if not source:
|
||
return None
|
||
|
||
# Если уже передан полный volid, оставляем его без изменений.
|
||
if ":" in source:
|
||
return source
|
||
|
||
# Сначала пытаемся найти точный volid среди шаблонов Proxmox.
|
||
# Это позволяет корректно обработать .tar.zst, .tar.gz и .tar.xz.
|
||
try:
|
||
for item in pve.list_lxc_templates():
|
||
if item.get("name") == source:
|
||
return item["volid"]
|
||
volid_name = item.get("volid", "").split("/")[-1]
|
||
if volid_name:
|
||
for extension in (".tar.zst", ".tar.gz", ".tar.xz"):
|
||
if volid_name.removesuffix(extension) == source:
|
||
return item["volid"]
|
||
except Exception as exc:
|
||
logger.warning("Не удалось сопоставить LXC-шаблон с Proxmox: %s", exc)
|
||
|
||
# Fallback для локального storage: короткое имя превращаем в volid.
|
||
for extension in (".tar.zst", ".tar.gz", ".tar.xz"):
|
||
if source.endswith(extension):
|
||
return f"local:vztmpl/{source}"
|
||
return f"local:vztmpl/{source}.tar.zst"
|
||
|
||
|
||
@router.get("", response_model=List[schemas.TemplateOut])
|
||
def list_templates(db: Session = Depends(get_db), _=Depends(get_current_user)):
|
||
"""Возвращает активные шаблоны, сохранённые в панели."""
|
||
return db.query(models.Template).filter(models.Template.is_active == True).all() # noqa: E712
|
||
|
||
|
||
@router.post("", response_model=schemas.TemplateOut)
|
||
def create_template(
|
||
payload: schemas.TemplateCreate,
|
||
db: Session = Depends(get_db),
|
||
_=Depends(require_admin),
|
||
):
|
||
"""Добавляет шаблон в каталог панели.
|
||
|
||
Для LXC автоматически нормализует короткое имя архива в Proxmox volid.
|
||
"""
|
||
data = payload.model_dump()
|
||
if data.get("guest_type") == models.GuestType.lxc:
|
||
data["source_vmid"] = None
|
||
data["source_template"] = _normalize_lxc_source(data.get("source_template"))
|
||
if not data["source_template"]:
|
||
from fastapi import HTTPException
|
||
raise HTTPException(status_code=400, detail="Для LXC нужно указать архивный шаблон")
|
||
else:
|
||
data["source_template"] = None
|
||
|
||
tpl = models.Template(**data)
|
||
db.add(tpl)
|
||
db.commit()
|
||
db.refresh(tpl)
|
||
return tpl
|
||
|
||
|
||
@router.get("/from-proxmox")
|
||
def list_proxmox_templates(_=Depends(require_admin)):
|
||
"""Возвращает VM-шаблоны и архивные LXC-шаблоны с Proxmox."""
|
||
try:
|
||
vm_templates = pve.list_vm_templates()
|
||
except Exception as exc:
|
||
logger.warning("Ошибка получения VM-шаблонов из Proxmox: %s", exc)
|
||
vm_templates = []
|
||
|
||
try:
|
||
lxc_templates = pve.list_lxc_templates()
|
||
except Exception as exc:
|
||
logger.warning("Ошибка получения архивов LXC-шаблонов из Proxmox: %s", exc)
|
||
lxc_templates = []
|
||
|
||
for template in lxc_templates:
|
||
template["source_kind"] = "archive"
|
||
|
||
return {
|
||
"vm_templates": vm_templates,
|
||
"lxc_templates": lxc_templates,
|
||
}
|
||
|
||
|
||
@router.delete("/{template_id}")
|
||
def deactivate_template(
|
||
template_id: int, db: Session = Depends(get_db), _=Depends(require_admin)
|
||
):
|
||
"""Деактивирует шаблон в каталоге панели."""
|
||
tpl = db.query(models.Template).filter(models.Template.id == template_id).first()
|
||
if tpl:
|
||
tpl.is_active = False
|
||
db.commit()
|
||
return {"ok": True}
|