fix(reinstall): use OSTemplate.vmid (not template_vmid) and convert memory bytes->MB

- AttributeError: OSTemplate has no 'template_vmid' field; the actual column is 'vmid'.
- Use OSTemplate.get_vmid_for_node() to support cross-node replicated templates,
  falling back to source_node/vmid otherwise.
- VMInstance.memory is stored in bytes, but Proxmox config expects MB.
This commit is contained in:
markmorado
2026-04-29 23:48:48 +02:00
parent 5169bf80a3
commit 89d70654fc
45 changed files with 2946 additions and 741 deletions

View File

@@ -10,7 +10,7 @@ import httpx
import websockets
from ...db import get_db
from ...models import ProxmoxServer, VMInstance, User, IPAMAllocation, IPAMNetwork, VMSnapshotArchive, ProxmoxTask
from ...models import ProxmoxServer, VMInstance, User, IPAMAllocation, IPAMNetwork, VMSnapshotArchive, ProxmoxTask, DeployTask
from ...schemas import ProxmoxServerCreate, ProxmoxServerUpdate, ProxmoxServerResponse
from ...proxmox_client import ProxmoxClient, get_proxmox_resources
from ...auth import get_current_user, PermissionChecker, require_permission, check_permission
@@ -211,17 +211,20 @@ def get_all_tasks(
"""
bulk_q = db.query(TaskQueue).filter(TaskQueue.user_id == current_user.id)
proxmox_q = db.query(ProxmoxTask).filter(ProxmoxTask.user_id == current_user.id)
deploy_q = db.query(DeployTask).filter(DeployTask.user_id == current_user.id)
if status_filter:
# Normalise statuses: proxmox uses running/completed/failed; bulk uses the same + pending/cancelled
bulk_q = bulk_q.filter(TaskQueue.status == status_filter)
proxmox_q = proxmox_q.filter(ProxmoxTask.status == status_filter)
deploy_q = deploy_q.filter(DeployTask.status == status_filter)
bulk_tasks = bulk_q.order_by(TaskQueue.created_at.desc()).limit(limit).all()
proxmox_tasks = proxmox_q.order_by(ProxmoxTask.created_at.desc()).limit(limit).all()
deploy_tasks = deploy_q.order_by(DeployTask.created_at.desc()).limit(limit).all()
all_tasks = sorted(
[t.to_dict() for t in bulk_tasks] + [t.to_dict() for t in proxmox_tasks],
[t.to_dict() for t in bulk_tasks] + [t.to_dict() for t in proxmox_tasks] + [t.to_dict() for t in deploy_tasks],
key=lambda t: t.get("created_at") or "",
reverse=True,
)[:limit]
@@ -245,7 +248,12 @@ def get_active_count(
ProxmoxTask.status == "running",
).scalar() or 0
return {"count": bulk_count + proxmox_count}
deploy_count = db.query(func.count(DeployTask.id)).filter(
DeployTask.user_id == current_user.id,
DeployTask.status.in_(["pending", "running"]),
).scalar() or 0
return {"count": bulk_count + proxmox_count + deploy_count}
# ─── Proxmox UPID task log from DB ──────────────────────────────────────────
@@ -292,6 +300,10 @@ def clear_completed_tasks(
ProxmoxTask.user_id == current_user.id,
ProxmoxTask.status.in_(done_statuses),
).delete(synchronize_session=False)
deploy_deleted = db.query(DeployTask).filter(
DeployTask.user_id == current_user.id,
DeployTask.status.in_(done_statuses),
).delete(synchronize_session=False)
db.commit()
return {"deleted": bulk_deleted + proxmox_deleted}
return {"deleted": bulk_deleted + proxmox_deleted + deploy_deleted}

View File

@@ -425,14 +425,27 @@ def reinstall_vm_endpoint(
from ...models import OSTemplate
tpl = db.query(OSTemplate).filter(OSTemplate.id == cached.template_id).first()
if not tpl or not tpl.template_vmid:
if not tpl or not tpl.vmid:
raise HTTPException(status_code=400, detail="Template not found or invalid")
# Resolve template VMID and source node (with cross-node replication support)
local_template_vmid = tpl.get_vmid_for_node(node)
if local_template_vmid:
template_source_node = node
template_vmid = local_template_vmid
else:
template_source_node = tpl.get_source_node() or tpl.node or node
template_vmid = tpl.vmid
if not template_vmid:
raise HTTPException(status_code=400, detail="Template VMID is not available for this node")
name = cached.name
cores = cached.cores
memory = cached.memory
description = cached.description
is_lxc = (cached.vm_type == 'lxc')
# VMInstance.memory is stored in bytes; Proxmox expects MB
memory_mb = int(memory) // (1024 * 1024) if memory else None
# 1) stop
try:
@@ -470,13 +483,13 @@ def reinstall_vm_endpoint(
try:
if is_lxc:
upid = client.clone_container(
node=tpl.node or node, vmid=tpl.template_vmid, new_vmid=vmid,
node=template_source_node, vmid=template_vmid, new_vmid=vmid,
hostname=name, full=True, target_node=node,
description=description,
)
else:
upid = client.clone_vm(
node=tpl.node or node, vmid=tpl.template_vmid, new_vmid=vmid,
node=template_source_node, vmid=template_vmid, new_vmid=vmid,
name=name, full=True, target_node=node,
description=description,
)
@@ -495,13 +508,13 @@ def reinstall_vm_endpoint(
if is_lxc:
cfg = {}
if cores: cfg['cores'] = cores
if memory: cfg['memory'] = int(memory)
if memory_mb: cfg['memory'] = memory_mb
if cfg:
client.update_container_config(node, vmid, cfg)
else:
cfg = {}
if cores: cfg['cores'] = cores
if memory: cfg['memory'] = int(memory)
if memory_mb: cfg['memory'] = memory_mb
if cfg:
client.update_vm_config(node, vmid, cfg)
except Exception as _ce:
@@ -512,7 +525,7 @@ def reinstall_vm_endpoint(
resource_type='lxc' if is_lxc else 'vm', resource_id=vmid,
username=current_user.username, resource_name=name,
server_id=server_id, server_name=server.name, node_name=node,
details={'template_id': tpl.id, 'template_vmid': tpl.template_vmid},
details={'template_id': tpl.id, 'template_vmid': template_vmid},
success=True,
)
return {"status": "success", "vmid": vmid, "upid": upid}