diff --git a/backend/app/routers/templates.py b/backend/app/routers/templates.py index 039bd95..5444c15 100644 --- a/backend/app/routers/templates.py +++ b/backend/app/routers/templates.py @@ -12,6 +12,47 @@ 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)): """Возвращает активные шаблоны, сохранённые в панели.""" @@ -24,8 +65,21 @@ def create_template( db: Session = Depends(get_db), _=Depends(require_admin), ): - """Добавляет шаблон в каталог панели.""" - tpl = models.Template(**payload.model_dump()) + """Добавляет шаблон в каталог панели. + + Для 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) @@ -34,10 +88,7 @@ def create_template( @router.get("/from-proxmox") def list_proxmox_templates(_=Depends(require_admin)): - """Возвращает VM-шаблоны и архивные LXC-шаблоны с Proxmox. - - Готовые LXC-контейнеры по VMID намеренно не показываются. - """ + """Возвращает VM-шаблоны и архивные LXC-шаблоны с Proxmox.""" try: vm_templates = pve.list_vm_templates() except Exception as exc: