fix(vm_provision_mixin): storage 'enabled' absent means enabled, not disabled

Proxmox's /storage API omits the "enabled" key entirely for storages that
were never explicitly toggled, rather than defaulting it to 1 — it isn't
present-and-falsy, it's just absent. Both _find_default_image_storage and
the snippet-storage discovery treated storage.get("enabled") as truthy-check,
so every storage without an explicit "enabled": 1 was silently excluded.

Confirmed live against a real Proxmox test server: local-lvm, local-zfs, and
fast-zfs all had content=images with no "enabled" key at all, causing
create_vm_from_cloud_init to always fail with "No storage with
content='images' found" despite multiple valid storages existing. All prior
tests used "enabled": 1 explicitly in their fixtures, masking the bug.

Fix: storage.get("enabled", 1) != 0 — absent or truthy means enabled, only
an explicit 0 excludes it. 4 new regression tests, 28 total pass.
This commit is contained in:
Christian Manivong
2026-07-07 12:12:44 +02:00
parent 9264cdcba9
commit 12135735cb
2 changed files with 109 additions and 3 deletions
+9 -3
View File
@@ -102,10 +102,15 @@ class ProxmoxVMProvisionMixin:
return local_path
def _find_default_image_storage(self) -> str:
"""Find a storage suitable for VM root disks (content includes 'images')."""
"""Find a storage suitable for VM root disks (content includes 'images').
Proxmox's /storage API omits the "enabled" field entirely for storages
that were never explicitly toggled — it is not present-and-falsy, it is
just absent, defaulting to enabled. Only an explicit 0 means disabled.
"""
for storage in self._api.storage.get():
content = storage.get("content", "")
if "images" in content and storage.get("enabled"):
if "images" in content and storage.get("enabled", 1) != 0:
return storage["storage"]
raise ValueError(
"No storage with content='images' found. Configure a storage for VM disks."
@@ -266,12 +271,13 @@ class ProxmoxVMProvisionMixin:
self._node_api().qemu(vmid).config.post(**config_args)
# Step 5: Verify snippet storage exists
# (enabled is absent-not-falsy on Proxmox — see _find_default_image_storage)
_logger.info("Checking for snippet storage...")
storages = self._api.storage.get()
snippet_storage = None
for storage in storages:
content = storage.get("content", "")
if "snippets" in content and storage.get("enabled"):
if "snippets" in content and storage.get("enabled", 1) != 0:
snippet_storage = storage["storage"]
break