diff --git a/backend/app/routers/instances.py b/backend/app/routers/instances.py index 8c44860..0f02367 100644 --- a/backend/app/routers/instances.py +++ b/backend/app/routers/instances.py @@ -1,11 +1,15 @@ -from fastapi import APIRouter, Depends +import logging + +from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy import select from sqlalchemy.orm import Session -from .. import models, schemas +from .. import models, proxmox_client as pve, schemas +from ..config import settings from ..database import get_db from ..deps import get_current_user +logger = logging.getLogger(__name__) router = APIRouter(prefix="/instances", tags=["instances"]) @@ -22,3 +26,79 @@ def list_instances( query = query.where(models.Instance.owner_id == user.id) query = query.order_by(models.Instance.created_at.desc()) return list(db.scalars(query)) + + +def _save_error(db: Session, instance: models.Instance, exc: Exception) -> None: + """Помечает инстанс ошибочным, не сохраняя секреты или детали API.""" + instance.status = models.InstanceStatus.error + db.commit() + logger.exception("Ошибка провижининга instance_id=%s: %s", instance.id, exc) + + +@router.post("", response_model=schemas.InstanceCreateOut, status_code=status.HTTP_201_CREATED) +def create_instance( + payload: schemas.InstanceCreate, + db: Session = Depends(get_db), + user: models.User = Depends(get_current_user), +): + """Создаёт и запускает VM/LXC из активного шаблона. + + Операция синхронная и может занимать несколько минут; очередь задач будет + добавлена следующим этапом. + """ + template = db.scalar( + select(models.Template).where( + models.Template.id == payload.template_id, + models.Template.is_active.is_(True), + ) + ) + if template is None: + raise HTTPException(status_code=404, detail="Активный шаблон не найден") + + vmid = pve.next_vmid() + instance = models.Instance( + name=payload.name, + vmid=vmid, + node=settings.pve_node, + guest_type=template.guest_type, + status=models.InstanceStatus.creating, + owner_id=user.id, + template_id=template.id, + ) + db.add(instance) + db.commit() + db.refresh(instance) + + try: + if template.guest_type == models.GuestType.vm: + pve.provision_vm( + source_vmid=template.source_vmid, + vmid=vmid, + name=payload.name, + cores=template.cores, + memory_mb=template.memory_mb, + disk_gb=template.disk_gb, + username=payload.username, + password=payload.password, + ) + else: + pve.provision_lxc( + source_template=template.source_template, + vmid=vmid, + name=payload.name, + cores=template.cores, + memory_mb=template.memory_mb, + disk_gb=template.disk_gb, + password=payload.password, + ) + except Exception as exc: + _save_error(db, instance, exc) + raise HTTPException(status_code=502, detail="Не удалось создать инстанс в Proxmox") + + instance.status = models.InstanceStatus.running + db.commit() + db.refresh(instance) + return schemas.InstanceCreateOut( + **schemas.InstanceOut.model_validate(instance).model_dump(), + initial_password=payload.password, + )