Обновление файла
This commit is contained in:
@@ -102,7 +102,18 @@ def create_instance(
|
|||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
user: models.User = Depends(get_current_user),
|
user: models.User = Depends(get_current_user),
|
||||||
):
|
):
|
||||||
"""Регистрирует один инстанс и запускает его создание в фоне."""
|
"""Регистрирует один инстанс и запускает его создание в фоне.
|
||||||
|
|
||||||
|
Бизнес-логика VMID:
|
||||||
|
1. Запрашиваем ``nextid`` у Proxmox (он возвращает следующий свободный VMID
|
||||||
|
в кластере — то есть тот, что реально не занят ни в Proxmox).
|
||||||
|
2. Проверяем в БД, что этот VMID не используется активным инстансом
|
||||||
|
(``status != deleted``). Если есть запись со статусом deleted — это
|
||||||
|
«мусор» от старых удалений, который мы НЕ должны учитывать (nextid
|
||||||
|
от Proxmox пришёл, значит в Proxmox этот VMID свободен).
|
||||||
|
3. Если конфликт — запрашиваем новый VMID через ``nextid``
|
||||||
|
(онлайн-цикл до 5 попыток).
|
||||||
|
"""
|
||||||
db.query(models.User).filter(models.User.id == user.id).with_for_update().one()
|
db.query(models.User).filter(models.User.id == user.id).with_for_update().one()
|
||||||
pending = (
|
pending = (
|
||||||
db.query(models.Instance)
|
db.query(models.Instance)
|
||||||
@@ -123,19 +134,36 @@ def create_instance(
|
|||||||
if not template:
|
if not template:
|
||||||
raise HTTPException(status_code=404, detail="Шаблон не найден")
|
raise HTTPException(status_code=404, detail="Шаблон не найден")
|
||||||
|
|
||||||
|
# Получаем VMID, который точно свободен: nextid из Proxmox + проверка БД.
|
||||||
|
# Если в БД висит «удалённая» запись с этим VMID (мусор), пропускаем —
|
||||||
|
# запрашиваем следующий. Proxmox при повторном nextid может вернуть тот же
|
||||||
|
# VMID, потому что он сам не знает про нашу БД. Поэтому цикл по Proxmox
|
||||||
|
# ограничен сверху, а грязные записи в БД лучше чистить заранее
|
||||||
|
# (разовая операция, см. README).
|
||||||
|
vmid = None
|
||||||
|
for _ in range(5):
|
||||||
try:
|
try:
|
||||||
vmid = pve.get_next_vmid()
|
candidate = pve.get_next_vmid()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.error("Proxmox недоступен при запросе nextid: %s", exc)
|
logger.error("Proxmox недоступен при запросе nextid: %s", exc)
|
||||||
raise HTTPException(status_code=502, detail=f"Proxmox недоступен: {exc}")
|
raise HTTPException(status_code=502, detail=f"Proxmox недоступен: {exc}")
|
||||||
|
|
||||||
active = (
|
active = (
|
||||||
db.query(models.Instance)
|
db.query(models.Instance)
|
||||||
.filter(models.Instance.vmid == vmid, models.Instance.status != models.InstanceStatus.deleted)
|
.filter(
|
||||||
|
models.Instance.vmid == candidate,
|
||||||
|
models.Instance.status != models.InstanceStatus.deleted,
|
||||||
|
)
|
||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
if active:
|
if not active:
|
||||||
raise HTTPException(status_code=409, detail=f"VMID {vmid} уже используется в панели")
|
vmid = candidate
|
||||||
|
break
|
||||||
|
if vmid is None:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=409,
|
||||||
|
detail="Не удалось получить свободный VMID — несколько попыток подряд вернули занятый VMID. "
|
||||||
|
"Очистите БД от записей со статусом 'deleted' или повторите позже.",
|
||||||
|
)
|
||||||
|
|
||||||
instance = models.Instance(
|
instance = models.Instance(
|
||||||
name=_sanitize_name(payload.name),
|
name=_sanitize_name(payload.name),
|
||||||
@@ -231,15 +259,19 @@ def instance_action(instance_id: int, payload: schemas.InstanceAction, db: Sessi
|
|||||||
|
|
||||||
@router.delete("/{instance_id}")
|
@router.delete("/{instance_id}")
|
||||||
def delete_instance(instance_id: int, db: Session = Depends(get_db), user: models.User = Depends(get_current_user)):
|
def delete_instance(instance_id: int, db: Session = Depends(get_db), user: models.User = Depends(get_current_user)):
|
||||||
"""Останавливает работающую VM/LXC и удаляет её из Proxmox."""
|
"""Останавливает работающую VM/LXC и удаляет её из Proxmox.
|
||||||
|
|
||||||
|
После успешного удаления в Proxmox — физически удаляет запись из БД,
|
||||||
|
чтобы VMID можно было использовать повторно (``nextid`` от Proxmox всегда
|
||||||
|
вернёт свободный ID, а наша БД не должна блокировать его из-за мусора).
|
||||||
|
"""
|
||||||
instance = db.query(models.Instance).filter(models.Instance.id == instance_id).first()
|
instance = db.query(models.Instance).filter(models.Instance.id == instance_id).first()
|
||||||
if not instance:
|
if not instance:
|
||||||
raise HTTPException(status_code=404, detail="VPS не найден")
|
raise HTTPException(status_code=404, detail="VPS не найден")
|
||||||
_ensure_owner(instance, user)
|
_ensure_owner(instance, user)
|
||||||
if instance.status == models.InstanceStatus.deleting:
|
if instance.status == models.InstanceStatus.deleting:
|
||||||
raise HTTPException(status_code=409, detail="Инстанс уже удаляется")
|
raise HTTPException(status_code=409, detail="Инстанс уже удаляется")
|
||||||
instance.status = models.InstanceStatus.deleting
|
vmid_for_log = instance.vmid # сохраняем для логов до удаления ORM-объекта
|
||||||
db.commit()
|
|
||||||
try:
|
try:
|
||||||
pve.delete_guest(instance.guest_type.value, instance.vmid, instance.node)
|
pve.delete_guest(instance.guest_type.value, instance.vmid, instance.node)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
@@ -247,6 +279,8 @@ def delete_instance(instance_id: int, db: Session = Depends(get_db), user: model
|
|||||||
instance.status = models.InstanceStatus.error
|
instance.status = models.InstanceStatus.error
|
||||||
db.commit()
|
db.commit()
|
||||||
raise HTTPException(status_code=502, detail=f"Ошибка удаления: {exc}")
|
raise HTTPException(status_code=502, detail=f"Ошибка удаления: {exc}")
|
||||||
instance.status = models.InstanceStatus.deleted
|
# Физическое удаление записи — VMID освобождается для повторного использования.
|
||||||
|
db.delete(instance)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
logger.info("Инстанс vmid=%s удалён из БД (был id=%s)", vmid_for_log, instance_id)
|
||||||
return {"ok": True}
|
return {"ok": True}
|
||||||
|
|||||||
Reference in New Issue
Block a user