[pve-devel] [PATCH v1 pve-esxi-import-tools 1/5] listvms: respect new type hints of pyVmomi package
Max R. Carrara
m.carrara at proxmox.com
Fri Jul 18 18:19:01 CEST 2025
This makes mypy on trixie happy again.
Make the type hints explicit; even though this is a little more
verbose, it should make any typing-related changes more visible in the
future. Also return (hopefully) sane defaults wherever possible.
Note that Python doesn't have something like "null-aware member
access" like JS does, so there isn't really any prettier version of
doing this.
(Besides, I have *never* seen as many `Optional`s in any Python
codebase as in the pyVmomi package's type stubs, let alone their
"on-demand loading" of types.)
Signed-off-by: Max R. Carrara <m.carrara at proxmox.com>
---
listvms.py | 95 ++++++++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 81 insertions(+), 14 deletions(-)
diff --git a/listvms.py b/listvms.py
index 44f207c..221d562 100755
--- a/listvms.py
+++ b/listvms.py
@@ -162,14 +162,29 @@ def get_datacenter_of_vm(vm: vim.VirtualMachine) -> vim.Datacenter | None:
def list_vms(service_instance: vim.ServiceInstance) -> list[vim.VirtualMachine]:
"""List all VMs on the ESXi/vCenter server."""
- content = service_instance.content
- vm_view: Any = content.viewManager.CreateContainerView(
+
+ content: vim.ServiceInstanceContent | None = service_instance.content
+
+ if content is None:
+ return []
+
+ view_manager: vim.view.ViewManager | None = content.viewManager
+
+ if view_manager is None:
+ return []
+
+ vm_view = view_manager.CreateContainerView(
content.rootFolder,
[vim.VirtualMachine],
True,
)
+
+ if vm_view is None:
+ return []
+
vms = vm_view.view
vm_view.Destroy()
+
return vms
@@ -180,23 +195,42 @@ def parse_file_path(path) -> tuple[str, str]:
return (datastore_name, relative_path)
-def get_vm_vmx_info(vm: vim.VirtualMachine) -> VmVmxInfo:
+def get_vm_vmx_info(vm: vim.VirtualMachine) -> VmVmxInfo | None:
"""Extract VMX file path and checksum from a VM object."""
- datastore_name, relative_vmx_path = parse_file_path(
- vm.config.files.vmPathName
+
+ config: vim.vm.ConfigInfo | None = vm.config
+
+ if config is None:
+ return None
+
+ files: vim.vm.FileInfo | None = config.files
+
+ if files is None:
+ return None
+
+ vm_path_name: str | None = files.vmPathName
+
+ if vm_path_name is None:
+ return None
+
+ datastore_name, relative_vmx_path = parse_file_path(vm_path_name)
+ checksum = (
+ config.vmxConfigChecksum.hex() if config.vmxConfigChecksum else "N/A"
)
return VmVmxInfo(
datastore=datastore_name,
path=relative_vmx_path,
- checksum=vm.config.vmxConfigChecksum.hex()
- if vm.config.vmxConfigChecksum
- else "N/A",
+ checksum=checksum,
)
def get_vm_disk_info(vm: vim.VirtualMachine) -> list[VmDiskInfo]:
- disks = []
+ disks: list[VmDiskInfo] = []
+
+ if vm.config is None:
+ return disks
+
for device in vm.config.hardware.device:
if isinstance(device, vim.vm.device.VirtualDisk):
try:
@@ -217,12 +251,27 @@ def get_all_datacenters(
service_instance: vim.ServiceInstance,
) -> list[vim.Datacenter]:
"""Retrieve all datacenters from the ESXi/vCenter server."""
- content = service_instance.content
- dc_view: Any = content.viewManager.CreateContainerView(
+
+ content: vim.ServiceInstanceContent | None = service_instance.content
+
+ if content is None:
+ return []
+
+ view_manager: vim.view.ViewManager | None = content.viewManager
+
+ if view_manager is None:
+ return []
+
+ dc_view = view_manager.CreateContainerView(
content.rootFolder, [vim.Datacenter], True
)
+
+ if dc_view is None:
+ return []
+
datacenters = dc_view.view
dc_view.Destroy()
+
return datacenters
@@ -242,13 +291,28 @@ def fetch_and_update_vm_data(vm: vim.VirtualMachine, data: dict[Any, Any]):
vms = data[datacenter.name].setdefault("vms", {})
datastores = data[datacenter.name].setdefault("datastores", {})
+ config: vim.vm.ConfigInfo | None = vm.config
+
+ if config is None:
+ return
+
+ vm_vmx_info: VmVmxInfo | None = get_vm_vmx_info(vm)
+
+ if vm_vmx_info is None:
+ return
+
+ runtime: vim.vm.RuntimeInfo | None = vm.runtime
+
+ if runtime is None:
+ return
+
vms[vm.name] = VmInfo(
- config=get_vm_vmx_info(vm),
+ config=vm_vmx_info,
disks=get_vm_disk_info(vm),
- power=str(vm.runtime.powerState),
+ power=str(runtime.powerState),
)
- datastores.update({ds.name: ds.url for ds in vm.config.datastoreUrl})
+ datastores.update({ds.name: ds.url for ds in config.datastoreUrl})
def is_vcls_agent_vm(vm: vim.VirtualMachine) -> bool:
@@ -261,6 +325,9 @@ def is_vcls_agent_vm(vm: vim.VirtualMachine) -> bool:
for cfg in vm.config.extraConfig)
def is_diskless_vm(vm: vim.VirtualMachine) -> bool:
+ if vm.config is None or vm.config.files is None:
+ return True
+
datastore_name, _ = parse_file_path(vm.config.files.vmPathName)
return not datastore_name
--
2.39.5
More information about the pve-devel
mailing list