Обновление файла
This commit is contained in:
@@ -1,16 +1,45 @@
|
|||||||
|
"""Роуты для работы с инстансами (VM/LXC) — создание, действия, удаление, статус, консоль."""
|
||||||
|
|
||||||
|
import logging
|
||||||
from typing import List
|
from typing import List
|
||||||
from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks
|
|
||||||
|
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException
|
||||||
from sqlalchemy.orm import Session, joinedload
|
from sqlalchemy.orm import Session, joinedload
|
||||||
from .. import models, schemas, proxmox_client as pve
|
|
||||||
|
from .. import models, proxmox_client as pve, schemas
|
||||||
from ..config import settings
|
from ..config import settings
|
||||||
from ..database import get_db
|
from ..database import SessionLocal, get_db
|
||||||
from ..deps import get_current_user
|
from ..deps import get_current_user
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
router = APIRouter(prefix="/instances", tags=["instances"])
|
router = APIRouter(prefix="/instances", tags=["instances"])
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- helpers ----------
|
||||||
|
|
||||||
|
def _ensure_owner(instance: models.Instance, user: models.User) -> None:
|
||||||
|
"""Проверяет, что инстанс принадлежит пользователю (или пользователь — админ)."""
|
||||||
|
if user.role == models.Role.admin:
|
||||||
|
return
|
||||||
|
if instance.owner_id != user.id:
|
||||||
|
raise HTTPException(status_code=404, detail="VPS не найден")
|
||||||
|
|
||||||
|
|
||||||
|
def _sanitize_name(raw: str) -> str:
|
||||||
|
"""Имя для Proxmox: только латиница/цифры/дефис, в нижнем регистре."""
|
||||||
|
cleaned = "".join(c for c in raw.strip() if c.isalnum() or c == "-").lower()
|
||||||
|
return cleaned or "vps"
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- list ----------
|
||||||
|
|
||||||
@router.get("", response_model=List[schemas.InstanceOut])
|
@router.get("", response_model=List[schemas.InstanceOut])
|
||||||
def list_my_instances(db: Session = Depends(get_db), user: models.User = Depends(get_current_user)):
|
def list_my_instances(
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
user: models.User = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
"""Список инстансов. Админ видит все, клиент — только свои."""
|
||||||
query = db.query(models.Instance).options(joinedload(models.Instance.template))
|
query = db.query(models.Instance).options(joinedload(models.Instance.template))
|
||||||
query = query.filter(models.Instance.status != models.InstanceStatus.deleted)
|
query = query.filter(models.Instance.status != models.InstanceStatus.deleted)
|
||||||
if user.role != models.Role.admin:
|
if user.role != models.Role.admin:
|
||||||
@@ -18,13 +47,19 @@ def list_my_instances(db: Session = Depends(get_db), user: models.User = Depends
|
|||||||
return query.order_by(models.Instance.created_at.desc()).all()
|
return query.order_by(models.Instance.created_at.desc()).all()
|
||||||
|
|
||||||
|
|
||||||
def _provision(instance_id: int, template_id: int, node: str):
|
# ---------- create ----------
|
||||||
"""Фоновое создание VPS через Proxmox API."""
|
|
||||||
from ..database import SessionLocal
|
def _provision(instance_id: int, template_id: int, node: str) -> None:
|
||||||
|
"""Фоновое создание VPS через Proxmox API.
|
||||||
|
|
||||||
|
Запускается через BackgroundTasks. Внутри — своя сессия БД,
|
||||||
|
потому что основной запрос уже вернул ответ клиенту.
|
||||||
|
"""
|
||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
try:
|
try:
|
||||||
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:
|
||||||
|
logger.warning("provision: instance %s не найден", instance_id)
|
||||||
return
|
return
|
||||||
template = db.query(models.Template).filter(models.Template.id == template_id).first()
|
template = db.query(models.Template).filter(models.Template.id == template_id).first()
|
||||||
if not template:
|
if not template:
|
||||||
@@ -32,29 +67,37 @@ def _provision(instance_id: int, template_id: int, node: str):
|
|||||||
instance.root_password = "ERROR: шаблон не найден"
|
instance.root_password = "ERROR: шаблон не найден"
|
||||||
db.commit()
|
db.commit()
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
clean_name = _sanitize_name(instance.name)
|
||||||
if template.guest_type == models.GuestType.vm:
|
if template.guest_type == models.GuestType.vm:
|
||||||
clean_name = ''.join(c for c in instance.name.strip() if c.isalnum() or c == '-').lower() or 'vm'
|
|
||||||
pve.clone_vm(template.source_vmid, instance.vmid, clean_name, node)
|
pve.clone_vm(template.source_vmid, instance.vmid, clean_name, node)
|
||||||
pve.resize_vm(instance.vmid, template.cores, template.memory_mb, node)
|
pve.resize_vm(instance.vmid, template.cores, template.memory_mb, node)
|
||||||
pve.resize_disk(instance.vmid, template.disk_gb, node)
|
pve.resize_disk(instance.vmid, template.disk_gb, node)
|
||||||
if instance.ciuser and instance.root_password:
|
if instance.ciuser and instance.root_password:
|
||||||
pve.configure_cloud_init(instance.vmid, instance.ciuser, instance.root_password, node)
|
pve.configure_cloud_init(
|
||||||
|
instance.vmid, instance.ciuser, instance.root_password, node
|
||||||
|
)
|
||||||
pve.guest_action("vm", instance.vmid, "start", node)
|
pve.guest_action("vm", instance.vmid, "start", node)
|
||||||
else:
|
else:
|
||||||
clean_name = ''.join(c for c in instance.name.strip() if c.isalnum() or c == '-').lower() or 'lxc'
|
|
||||||
password = pve.create_lxc(
|
password = pve.create_lxc(
|
||||||
new_vmid=instance.vmid, name=clean_name,
|
new_vmid=instance.vmid,
|
||||||
|
name=clean_name,
|
||||||
template_volid=template.source_template,
|
template_volid=template.source_template,
|
||||||
cores=template.cores, memory_mb=template.memory_mb,
|
cores=template.cores,
|
||||||
disk_gb=template.disk_gb, node=node,
|
memory_mb=template.memory_mb,
|
||||||
|
disk_gb=template.disk_gb,
|
||||||
|
node=node,
|
||||||
)
|
)
|
||||||
instance.root_password = password
|
instance.root_password = password
|
||||||
pve.guest_action("lxc", instance.vmid, "start", node)
|
pve.guest_action("lxc", instance.vmid, "start", node)
|
||||||
instance.status = models.InstanceStatus.running
|
instance.status = models.InstanceStatus.running
|
||||||
|
logger.info("instance %s (vmid=%s) успешно создан и запущен", instance.id, instance.vmid)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
logger.exception("Ошибка провижининга instance %s", instance_id)
|
||||||
instance.status = models.InstanceStatus.error
|
instance.status = models.InstanceStatus.error
|
||||||
instance.root_password = f"ERROR: {exc}"
|
# Не возвращаем длинный trace в root_password — только краткое сообщение.
|
||||||
|
instance.root_password = f"ERROR: {type(exc).__name__}: {exc}"
|
||||||
db.commit()
|
db.commit()
|
||||||
finally:
|
finally:
|
||||||
db.close()
|
db.close()
|
||||||
@@ -67,46 +110,68 @@ 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),
|
||||||
):
|
):
|
||||||
template = db.query(models.Template).filter(
|
"""Создаёт инстанс из шаблона. VMID берётся у Proxmox."""
|
||||||
models.Template.id == payload.template_id, models.Template.is_active == True
|
template = (
|
||||||
).first()
|
db.query(models.Template)
|
||||||
|
.filter(models.Template.id == payload.template_id, models.Template.is_active == True) # noqa: E712
|
||||||
|
.first()
|
||||||
|
)
|
||||||
if not template:
|
if not template:
|
||||||
raise HTTPException(status_code=404, detail="Шаблон не найден")
|
raise HTTPException(status_code=404, detail="Шаблон не найден")
|
||||||
|
|
||||||
|
# Запрашиваем VMID у Proxmox. Если в БД уже есть запись с таким VMID
|
||||||
|
# в "мёртвом" статусе — удаляем её, иначе insert упадёт с unique violation.
|
||||||
|
vmid = None
|
||||||
for _ in range(100):
|
for _ in range(100):
|
||||||
try:
|
try:
|
||||||
vmid = pve.get_next_vmid()
|
vmid = pve.get_next_vmid()
|
||||||
except Exception as exc:
|
except Exception as 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}")
|
||||||
exists = db.query(models.Instance).filter(
|
|
||||||
|
active = (
|
||||||
|
db.query(models.Instance)
|
||||||
|
.filter(
|
||||||
models.Instance.vmid == vmid,
|
models.Instance.vmid == vmid,
|
||||||
models.Instance.status != models.InstanceStatus.deleted,
|
models.Instance.status != models.InstanceStatus.deleted,
|
||||||
).first()
|
)
|
||||||
if not exists:
|
.first()
|
||||||
|
)
|
||||||
|
if not active:
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
raise HTTPException(status_code=409, detail="Не удалось найти свободный VMID")
|
raise HTTPException(status_code=409, detail="Не удалось найти свободный VMID")
|
||||||
|
|
||||||
clean_name = ''.join(c for c in payload.name.strip() if c.isalnum() or c == '-').lower() or 'vps'
|
if vmid is None:
|
||||||
|
raise HTTPException(status_code=502, detail="Не удалось получить VMID от Proxmox")
|
||||||
|
|
||||||
# Proxmox переиспользует освободившиеся vmid — вычищаем мёртвые записи,
|
# Чистим «мёртвые» записи с тем же VMID.
|
||||||
# иначе INSERT упадёт с duplicate key по ix_instances_vmid
|
stale = (
|
||||||
stale = db.query(models.Instance).filter(
|
db.query(models.Instance)
|
||||||
|
.filter(
|
||||||
models.Instance.vmid == vmid,
|
models.Instance.vmid == vmid,
|
||||||
models.Instance.status.in_([
|
models.Instance.status.in_([
|
||||||
models.InstanceStatus.error,
|
models.InstanceStatus.error,
|
||||||
models.InstanceStatus.deleted,
|
models.InstanceStatus.deleted,
|
||||||
models.InstanceStatus.deleting,
|
models.InstanceStatus.deleting,
|
||||||
]),
|
]),
|
||||||
).all()
|
)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
for s in stale:
|
for s in stale:
|
||||||
db.delete(s)
|
db.delete(s)
|
||||||
if stale:
|
if stale:
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
|
clean_name = _sanitize_name(payload.name)
|
||||||
instance = models.Instance(
|
instance = models.Instance(
|
||||||
name=clean_name, vmid=vmid, node=settings.pve_node,
|
name=clean_name,
|
||||||
guest_type=template.guest_type, status=models.InstanceStatus.creating,
|
vmid=vmid,
|
||||||
owner_id=user.id, template_id=template.id,
|
node=settings.pve_node,
|
||||||
|
guest_type=template.guest_type,
|
||||||
|
status=models.InstanceStatus.creating,
|
||||||
|
owner_id=user.id,
|
||||||
|
template_id=template.id,
|
||||||
ciuser=payload.ciuser or None,
|
ciuser=payload.ciuser or None,
|
||||||
root_password=payload.cipassword or None,
|
root_password=payload.cipassword or None,
|
||||||
)
|
)
|
||||||
@@ -117,46 +182,95 @@ def create_instance(
|
|||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- single-instance endpoints ----------
|
||||||
|
|
||||||
@router.get("/{instance_id}", response_model=schemas.InstanceOut)
|
@router.get("/{instance_id}", response_model=schemas.InstanceOut)
|
||||||
def get_instance(instance_id: int, db: Session = Depends(get_db), user: models.User = Depends(get_current_user)):
|
def get_instance(
|
||||||
|
instance_id: int,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
user: models.User = Depends(get_current_user),
|
||||||
|
):
|
||||||
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)
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{instance_id}/status")
|
@router.get("/{instance_id}/status")
|
||||||
def get_instance_status(instance_id: int, db: Session = Depends(get_db), user: models.User = Depends(get_current_user)):
|
def get_instance_status(
|
||||||
|
instance_id: int,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
user: models.User = Depends(get_current_user),
|
||||||
|
):
|
||||||
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)
|
||||||
try:
|
try:
|
||||||
return pve.get_status(instance.guest_type.value, instance.vmid, instance.node)
|
return pve.get_status(instance.guest_type.value, instance.vmid, instance.node)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
logger.warning("get_status: proxmox недоступен для vmid=%s: %s", instance.vmid, exc)
|
||||||
raise HTTPException(status_code=502, detail=f"Proxmox недоступен: {exc}")
|
raise HTTPException(status_code=502, detail=f"Proxmox недоступен: {exc}")
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{instance_id}/ip")
|
@router.get("/{instance_id}/ip", response_model=schemas.InstanceIPOut)
|
||||||
def get_instance_ip(instance_id: int, db: Session = Depends(get_db), user: models.User = Depends(get_current_user)):
|
def get_instance_ip(
|
||||||
|
instance_id: int,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
user: models.User = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
"""Возвращает IP-адрес инстанса (через QEMU Guest Agent или LXC-интерфейсы)."""
|
||||||
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)
|
||||||
try:
|
try:
|
||||||
ip = pve.get_instance_ip(instance.vmid, instance.node)
|
ip = pve.get_instance_ip(instance.vmid, instance.node)
|
||||||
return {"ip": ip}
|
return schemas.InstanceIPOut(ip=ip)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
return {"ip": f"ошибка: {exc}"}
|
logger.warning("get_instance_ip: vmid=%s: %s", instance.vmid, exc)
|
||||||
|
return schemas.InstanceIPOut(ip=f"ошибка: {exc}")
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{instance_id}/live")
|
||||||
|
def get_instance_live(
|
||||||
|
instance_id: int,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
user: models.User = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
"""Live-показатели CPU/RAM/uptime."""
|
||||||
|
instance = db.query(models.Instance).filter(models.Instance.id == instance_id).first()
|
||||||
|
if not instance:
|
||||||
|
raise HTTPException(status_code=404, detail="VPS не найден")
|
||||||
|
_ensure_owner(instance, user)
|
||||||
|
try:
|
||||||
|
return pve.get_live_stats(instance.vmid, instance.node)
|
||||||
|
except Exception as exc:
|
||||||
|
logger.warning("get_instance_live: vmid=%s: %s", instance.vmid, exc)
|
||||||
|
return {"cpu": 0, "mem_used": 0, "mem_total": 0, "uptime": 0, "status": "error", "error": str(exc)}
|
||||||
|
|
||||||
|
|
||||||
@router.post("/{instance_id}/action")
|
@router.post("/{instance_id}/action")
|
||||||
def instance_action(instance_id: int, payload: schemas.InstanceAction, db: Session = Depends(get_db), user: models.User = Depends(get_current_user)):
|
def instance_action(
|
||||||
|
instance_id: int,
|
||||||
|
payload: schemas.InstanceAction,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
user: models.User = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
"""Действие над инстансом: start | stop | shutdown | reboot."""
|
||||||
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)
|
||||||
try:
|
try:
|
||||||
pve.guest_action(instance.guest_type.value, instance.vmid, payload.action, instance.node)
|
pve.guest_action(instance.guest_type.value, instance.vmid, payload.action, instance.node)
|
||||||
|
except ValueError as exc:
|
||||||
|
raise HTTPException(status_code=400, detail=str(exc))
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
logger.warning("instance_action: vmid=%s action=%s: %s", instance.vmid, payload.action, exc)
|
||||||
raise HTTPException(status_code=502, detail=f"Ошибка: {exc}")
|
raise HTTPException(status_code=502, detail=f"Ошибка: {exc}")
|
||||||
|
|
||||||
if payload.action == "start":
|
if payload.action == "start":
|
||||||
instance.status = models.InstanceStatus.running
|
instance.status = models.InstanceStatus.running
|
||||||
elif payload.action in ("stop", "shutdown"):
|
elif payload.action in ("stop", "shutdown"):
|
||||||
@@ -165,53 +279,21 @@ def instance_action(instance_id: int, payload: schemas.InstanceAction, db: Sessi
|
|||||||
return {"ok": True}
|
return {"ok": True}
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{instance_id}/ip")
|
|
||||||
def get_instance_ip(instance_id: int, db: Session = Depends(get_db), user: models.User = Depends(get_current_user)):
|
|
||||||
"""Возвращает IP-адрес инстанса."""
|
|
||||||
instance = db.query(models.Instance).filter(models.Instance.id == instance_id).first()
|
|
||||||
if not instance:
|
|
||||||
raise HTTPException(status_code=404, detail="VPS не найден")
|
|
||||||
if user.role != models.Role.admin and instance.owner_id != user.id:
|
|
||||||
raise HTTPException(status_code=403, detail="Нет доступа")
|
|
||||||
try:
|
|
||||||
ip = pve.get_instance_ip(instance.vmid, instance.node)
|
|
||||||
return {"ip": ip}
|
|
||||||
except Exception as exc:
|
|
||||||
return {"ip": f"ошибка: {exc}"}
|
|
||||||
|
|
||||||
@router.get("/{instance_id}/ip")
|
|
||||||
def get_instance_ip(instance_id: int, db: Session = Depends(get_db), user: models.User = Depends(get_current_user)):
|
|
||||||
"""Возвращает IP-адрес инстанса."""
|
|
||||||
instance = db.query(models.Instance).filter(models.Instance.id == instance_id).first()
|
|
||||||
if not instance:
|
|
||||||
raise HTTPException(status_code=404, detail="VPS не найден")
|
|
||||||
if user.role != models.Role.admin and instance.owner_id != user.id:
|
|
||||||
raise HTTPException(status_code=403, detail="Нет доступа")
|
|
||||||
try:
|
|
||||||
ip = pve.get_instance_ip(instance.vmid, instance.node)
|
|
||||||
return {"ip": ip}
|
|
||||||
except Exception as exc:
|
|
||||||
return {"ip": f"ошибка: {exc}"}
|
|
||||||
|
|
||||||
@router.get("/{instance_id}/live")
|
|
||||||
def get_instance_live(instance_id: int, db: Session = Depends(get_db), user: models.User = Depends(get_current_user)):
|
|
||||||
"""Live CPU/RAM/Memory показатели."""
|
|
||||||
instance = db.query(models.Instance).filter(models.Instance.id == instance_id).first()
|
|
||||||
if not instance:
|
|
||||||
raise HTTPException(status_code=404, detail="VPS не найден")
|
|
||||||
try:
|
|
||||||
return pve.get_live_stats(instance.vmid, instance.node)
|
|
||||||
except Exception as exc:
|
|
||||||
return {"cpu": 0, "mem_used": 0, "mem_total": 0, "uptime": 0, "status": "error", "error": str(exc)}
|
|
||||||
|
|
||||||
@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 и помечает запись как deleted."""
|
||||||
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)
|
||||||
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:
|
||||||
|
logger.warning("delete_instance: vmid=%s: %s", instance.vmid, exc)
|
||||||
raise HTTPException(status_code=502, detail=f"Ошибка удаления: {exc}")
|
raise HTTPException(status_code=502, detail=f"Ошибка удаления: {exc}")
|
||||||
instance.status = models.InstanceStatus.deleted
|
instance.status = models.InstanceStatus.deleted
|
||||||
db.commit()
|
db.commit()
|
||||||
@@ -219,13 +301,20 @@ def delete_instance(instance_id: int, db: Session = Depends(get_db), user: model
|
|||||||
|
|
||||||
|
|
||||||
@router.get("/{instance_id}/console")
|
@router.get("/{instance_id}/console")
|
||||||
def instance_console(instance_id: int, db: Session = Depends(get_db), user: models.User = Depends(get_current_user)):
|
def instance_console(
|
||||||
|
instance_id: int,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
user: models.User = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
"""Возвращает данные для подключения noVNC-консоли через websocket-прокси."""
|
||||||
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)
|
||||||
try:
|
try:
|
||||||
ticket = pve.get_vnc_ticket(instance.guest_type.value, instance.vmid, instance.node)
|
ticket = pve.get_vnc_ticket(instance.guest_type.value, instance.vmid, instance.node)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
logger.warning("instance_console: vmid=%s: %s", instance.vmid, exc)
|
||||||
raise HTTPException(status_code=502, detail=f"Консоль недоступна: {exc}")
|
raise HTTPException(status_code=502, detail=f"Консоль недоступна: {exc}")
|
||||||
ticket["instance_id"] = instance.id
|
ticket["instance_id"] = instance.id
|
||||||
return ticket
|
return ticket
|
||||||
|
|||||||
Reference in New Issue
Block a user