fix(vm_provision_mixin): destroy_vm's delete call rejected by Proxmox (400)

Passed destroy_unreferenced_disks (underscore) as a kwarg to proxmoxer's
delete(), but Proxmox's actual DELETE /nodes/{node}/qemu/{vmid} parameter
is hyphenated (destroy-unreferenced-disks). proxmoxer forwards kwargs to
the request verbatim with no underscore-to-hyphen translation, so Proxmox
rejected every call with "property is not defined in schema" before ever
touching the VM — the VM stayed fully intact (config, disks) despite the
caller believing destroy had at least been attempted. Fixed by building
the params as a dict (bypassing the Python-identifier restriction) with
the correct hyphenated key.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Christian Manivong
2026-07-08 19:54:05 +02:00
co-authored by Claude Sonnet 5
parent ee2f0e94ff
commit 3181ade728
2 changed files with 14 additions and 4 deletions
+9 -4
View File
@@ -476,11 +476,16 @@ class ProxmoxVMProvisionMixin:
_logger.debug(f"VM {vmid} stop failed (may already be stopped): {e}") _logger.debug(f"VM {vmid} stop failed (may already be stopped): {e}")
# Step 2: Delete VM # Step 2: Delete VM
# Proxmox's API parameter is hyphenated (destroy-unreferenced-disks),
# not a valid Python identifier — proxmoxer forwards kwargs to the
# request verbatim with no underscore-to-hyphen translation, so this
# must be built as a dict and unpacked rather than passed as a kwarg.
_logger.debug(f"Deleting VM {vmid} configuration and disks") _logger.debug(f"Deleting VM {vmid} configuration and disks")
self._node_api().qemu(vmid_int).delete( delete_params = {
purge=1, "purge": 1,
destroy_unreferenced_disks=1 if remove_disk else 0, "destroy-unreferenced-disks": 1 if remove_disk else 0,
) }
self._node_api().qemu(vmid_int).delete(**delete_params)
# Step 3: Clean up Cloud-Init snippets # Step 3: Clean up Cloud-Init snippets
# (This is best-effort; snippet files may be unreachable if storage is unavailable) # (This is best-effort; snippet files may be unreachable if storage is unavailable)
+5
View File
@@ -451,6 +451,11 @@ def test_destroy_vm_success():
mixin.destroy_vm("101", remove_disk=True, timeout=60) mixin.destroy_vm("101", remove_disk=True, timeout=60)
assert mock_vm.delete.called assert mock_vm.delete.called
# Proxmox's API parameter is hyphenated; passing the underscore form
# (a Python-identifier-friendly typo) gets silently rejected by Proxmox
# with a 400 "property is not defined in schema" instead of deleting.
_, delete_kwargs = mock_vm.delete.call_args
assert delete_kwargs == {"purge": 1, "destroy-unreferenced-disks": 1}
def test_destroy_vm_already_stopped(): def test_destroy_vm_already_stopped():