From f9b8a54673ca70cd99c153114e4c88844bb002e0 Mon Sep 17 00:00:00 2001 From: Christian Manivong Date: Tue, 7 Jul 2026 22:35:33 +0200 Subject: [PATCH] feat(hypervisor): add StorageTargetDict and get_image_storages(), storage param on create_vm_from_cloud_init Lets callers select which node-available storage pool a new VM's root disk lands on, instead of always trusting the driver's auto-detected default. --- napalm_device_types/hypervisor.py | 24 ++++++++++++++++++++++++ napalm_device_types/models.py | 12 ++++++++++++ 2 files changed, 36 insertions(+) diff --git a/napalm_device_types/hypervisor.py b/napalm_device_types/hypervisor.py index 594ccc8..e543f09 100644 --- a/napalm_device_types/hypervisor.py +++ b/napalm_device_types/hypervisor.py @@ -19,6 +19,7 @@ from napalm_device_types.models import ( NetworkTargetDict, PackageDict, SnapshotDict, + StorageTargetDict, StorageVolumeDict, VMConfigDict, VMDict, @@ -556,6 +557,7 @@ class HypervisorDriver(DeviceTypeDriver): image_checksum: str | None = None, ssh_public_keys: List[str] | None = None, disk_resize_gb: int | None = None, + storage: str | None = None, download_timeout: int = 300, timeout: int = 180, ) -> VMProvisionResultDict: @@ -591,6 +593,10 @@ class HypervisorDriver(DeviceTypeDriver): If None or empty, no SSH key injection is performed. disk_resize_gb (int | None) - resize root disk to this size in GB. If None, disk remains the downloaded image's native size. Default None. + storage (string | None) - name of the storage pool to place the root + disk on (a name returned by ``get_image_storages()``). If None, + the driver auto-detects the first enabled, node-available storage + whose content includes "images". download_timeout (int) - maximum seconds to wait for the image download (skipped entirely if already cached on the hypervisor). Default 300. timeout (int) - maximum seconds to wait for the remaining provisioning @@ -688,3 +694,21 @@ class HypervisorDriver(DeviceTypeDriver): per-kind rules). """ raise NotImplementedError + + def get_image_storages(self) -> List[StorageTargetDict]: + """ + List the storage pools a new VM's root disk may be placed on, scoped to + the specific node the VM will be created on. + + Returns only storages that are actually usable for this purpose right + now: content includes "images", administratively enabled, and — for + hypervisors where storage can be restricted to a subset of cluster + nodes — available on this node specifically. A storage configured + cluster-wide but restricted to other nodes must not appear here, since + passing its name to ``create_vm_from_cloud_init(storage=...)`` would fail. + + Returns: + List[StorageTargetDict] - each entry's ``name`` is directly usable + as ``create_vm_from_cloud_init``'s ``storage`` argument. + """ + raise NotImplementedError diff --git a/napalm_device_types/models.py b/napalm_device_types/models.py index 8955b10..1ff9842 100644 --- a/napalm_device_types/models.py +++ b/napalm_device_types/models.py @@ -749,3 +749,15 @@ class NetworkTargetDict(TypedDict): kind: str # "bridge" or "vnet" vlan_aware: bool # True if a NICConfigDict.vlan_tag may be set on top of this target fixed_vlan_tag: NotRequired[int | None] # vnet only: the VLAN ID already baked into it + + +class StorageTargetDict(TypedDict): + """A selectable storage pool for a new VM's root disk (``create_vm_from_cloud_init``'s + ``storage`` argument), scoped to the specific node the VM will be created on — + a storage restricted to other cluster nodes must not appear here. + """ + + name: str # Storage pool name, usable directly as create_vm_from_cloud_init(storage=...) + type: str # Backend type: "dir", "lvmthin", "zfspool", "nfs", etc. + total_gb: float # Total capacity in gigabytes + available_gb: float # Free capacity in gigabytes