Archived
Добавлено удаление инстансов и обработка ошибок VMID
This commit is contained in:
@@ -8,62 +8,48 @@ from .. import models, proxmox_client as pve, schemas
|
|||||||
from ..config import settings
|
from ..config import settings
|
||||||
from ..database import get_db
|
from ..database import get_db
|
||||||
from ..deps import get_current_user
|
from ..deps import get_current_user
|
||||||
|
from ..proxmox_actions import delete_guest
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
router = APIRouter(prefix="/instances", tags=["instances"])
|
router = APIRouter(prefix="/instances", tags=["instances"])
|
||||||
|
|
||||||
|
|
||||||
@router.get("", response_model=list[schemas.InstanceOut])
|
@router.get("", response_model=list[schemas.InstanceOut])
|
||||||
def list_instances(
|
def list_instances(db: Session = Depends(get_db), user: models.User = Depends(get_current_user)):
|
||||||
db: Session = Depends(get_db),
|
|
||||||
user: models.User = Depends(get_current_user),
|
|
||||||
):
|
|
||||||
"""Клиент видит только свои инстансы, администратор — все."""
|
"""Клиент видит только свои инстансы, администратор — все."""
|
||||||
query = select(models.Instance).where(
|
query = select(models.Instance).where(models.Instance.status != models.InstanceStatus.deleted)
|
||||||
models.Instance.status != models.InstanceStatus.deleted
|
|
||||||
)
|
|
||||||
if user.role != models.UserRole.admin:
|
if user.role != models.UserRole.admin:
|
||||||
query = query.where(models.Instance.owner_id == user.id)
|
query = query.where(models.Instance.owner_id == user.id)
|
||||||
query = query.order_by(models.Instance.created_at.desc())
|
return list(db.scalars(query.order_by(models.Instance.created_at.desc())))
|
||||||
return list(db.scalars(query))
|
|
||||||
|
|
||||||
|
|
||||||
def _save_error(db: Session, instance: models.Instance, exc: Exception) -> None:
|
def _save_error(db: Session, instance: models.Instance, exc: Exception) -> None:
|
||||||
"""Помечает инстанс ошибочным, не сохраняя секреты или детали API."""
|
"""Помечает инстанс ошибочным без сохранения деталей Proxmox пользователю."""
|
||||||
instance.status = models.InstanceStatus.error
|
instance.status = models.InstanceStatus.error
|
||||||
db.commit()
|
db.commit()
|
||||||
logger.exception("Ошибка провижининга instance_id=%s: %s", instance.id, exc)
|
logger.exception("Ошибка провижининга instance_id=%s: %s", instance.id, exc)
|
||||||
|
|
||||||
|
|
||||||
@router.post("", response_model=schemas.InstanceCreateOut, status_code=status.HTTP_201_CREATED)
|
@router.post("", response_model=schemas.InstanceCreateOut, status_code=status.HTTP_201_CREATED)
|
||||||
def create_instance(
|
def create_instance(payload: schemas.InstanceCreate, db: Session = Depends(get_db), user: models.User = Depends(get_current_user)):
|
||||||
payload: schemas.InstanceCreate,
|
"""Создаёт и запускает VM/LXC из активного шаблона."""
|
||||||
db: Session = Depends(get_db),
|
template = db.scalar(select(models.Template).where(
|
||||||
user: models.User = Depends(get_current_user),
|
models.Template.id == payload.template_id,
|
||||||
):
|
models.Template.is_active.is_(True),
|
||||||
"""Создаёт и запускает 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:
|
if template is None:
|
||||||
raise HTTPException(status_code=404, detail="Активный шаблон не найден")
|
raise HTTPException(status_code=404, detail="Активный шаблон не найден")
|
||||||
|
|
||||||
vmid = pve.next_vmid()
|
try:
|
||||||
|
vmid = pve.next_vmid()
|
||||||
|
except Exception as exc:
|
||||||
|
logger.exception("Не удалось получить VMID из Proxmox: %s", exc)
|
||||||
|
raise HTTPException(status_code=502, detail="Proxmox недоступен")
|
||||||
|
|
||||||
instance = models.Instance(
|
instance = models.Instance(
|
||||||
name=payload.name,
|
name=payload.name, vmid=vmid, node=settings.pve_node,
|
||||||
vmid=vmid,
|
guest_type=template.guest_type, status=models.InstanceStatus.creating,
|
||||||
node=settings.pve_node,
|
owner_id=user.id, template_id=template.id,
|
||||||
guest_type=template.guest_type,
|
|
||||||
status=models.InstanceStatus.creating,
|
|
||||||
owner_id=user.id,
|
|
||||||
template_id=template.id,
|
|
||||||
)
|
)
|
||||||
db.add(instance)
|
db.add(instance)
|
||||||
db.commit()
|
db.commit()
|
||||||
@@ -72,23 +58,14 @@ def create_instance(
|
|||||||
try:
|
try:
|
||||||
if template.guest_type == models.GuestType.vm:
|
if template.guest_type == models.GuestType.vm:
|
||||||
pve.provision_vm(
|
pve.provision_vm(
|
||||||
source_vmid=template.source_vmid,
|
source_vmid=template.source_vmid, vmid=vmid, name=payload.name,
|
||||||
vmid=vmid,
|
cores=template.cores, memory_mb=template.memory_mb, disk_gb=template.disk_gb,
|
||||||
name=payload.name,
|
username=payload.username, password=payload.password,
|
||||||
cores=template.cores,
|
|
||||||
memory_mb=template.memory_mb,
|
|
||||||
disk_gb=template.disk_gb,
|
|
||||||
username=payload.username,
|
|
||||||
password=payload.password,
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
pve.provision_lxc(
|
pve.provision_lxc(
|
||||||
source_template=template.source_template,
|
source_template=template.source_template, vmid=vmid, name=payload.name,
|
||||||
vmid=vmid,
|
cores=template.cores, memory_mb=template.memory_mb, disk_gb=template.disk_gb,
|
||||||
name=payload.name,
|
|
||||||
cores=template.cores,
|
|
||||||
memory_mb=template.memory_mb,
|
|
||||||
disk_gb=template.disk_gb,
|
|
||||||
password=payload.password,
|
password=payload.password,
|
||||||
)
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
@@ -102,3 +79,29 @@ def create_instance(
|
|||||||
**schemas.InstanceOut.model_validate(instance).model_dump(),
|
**schemas.InstanceOut.model_validate(instance).model_dump(),
|
||||||
initial_password=payload.password,
|
initial_password=payload.password,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete("/{instance_id}")
|
||||||
|
def delete_instance(instance_id: int, db: Session = Depends(get_db), user: models.User = Depends(get_current_user)):
|
||||||
|
"""Удаляет принадлежащую пользователю VM/LXC и отмечает запись deleted."""
|
||||||
|
instance = db.get(models.Instance, instance_id)
|
||||||
|
if instance is None or instance.status == models.InstanceStatus.deleted:
|
||||||
|
raise HTTPException(status_code=404, detail="Инстанс не найден")
|
||||||
|
if user.role != models.UserRole.admin and instance.owner_id != user.id:
|
||||||
|
raise HTTPException(status_code=404, detail="Инстанс не найден")
|
||||||
|
if instance.vmid is None:
|
||||||
|
raise HTTPException(status_code=409, detail="У инстанса отсутствует VMID")
|
||||||
|
|
||||||
|
instance.status = models.InstanceStatus.deleting
|
||||||
|
db.commit()
|
||||||
|
try:
|
||||||
|
delete_guest(instance.guest_type.value, instance.vmid)
|
||||||
|
except Exception as exc:
|
||||||
|
instance.status = models.InstanceStatus.error
|
||||||
|
db.commit()
|
||||||
|
logger.exception("Ошибка удаления instance_id=%s: %s", instance.id, exc)
|
||||||
|
raise HTTPException(status_code=502, detail="Не удалось удалить инстанс в Proxmox")
|
||||||
|
|
||||||
|
instance.status = models.InstanceStatus.deleted
|
||||||
|
db.commit()
|
||||||
|
return {"ok": True}
|
||||||
|
|||||||
Reference in New Issue
Block a user