From e71610292a0fb26fa335a1e82b3ccfb63b2fa8b6 Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Mon, 8 Jun 2026 06:15:13 -0600 Subject: [PATCH] Fix detach_iso: eject the ISO-bearing drive(s), not the first CD/DVD detach_iso used _find_cdrom (first drive), so on VMs with multiple CD/DVD drives (e.g. Cisco CUCM's two) it 'ejected' an already-empty drive and reported previous_iso=null while the mounted ISO stayed put. Now it finds every drive with an IsoBackingInfo and ejects them all, reporting each. --- src/mcvsphere/mixins/disk_management.py | 68 +++++++++++++++---------- 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/src/mcvsphere/mixins/disk_management.py b/src/mcvsphere/mixins/disk_management.py index 3975028..26096ac 100644 --- a/src/mcvsphere/mixins/disk_management.py +++ b/src/mcvsphere/mixins/disk_management.py @@ -511,42 +511,56 @@ class DiskManagementMixin(VSphereMixin): if not vm: raise ValueError(f"VM '{vm_name}' not found") - cdrom = self._find_cdrom(vm) - if not cdrom: + cdroms = [ + d + for d in vm.config.hardware.device + if isinstance(d, vim.vm.device.VirtualCdrom) + ] + if not cdroms: raise ValueError(f"No CD/DVD drive found on VM '{vm_name}'") - # Get current ISO path for reporting - old_iso = None - if hasattr(cdrom.backing, "fileName"): - old_iso = cdrom.backing.fileName + # Eject every drive that actually has an ISO mounted (a VM may have + # several CD/DVD drives; the ISO is rarely on the first one). + iso_cdroms = [ + c + for c in cdroms + if isinstance(c.backing, vim.vm.device.VirtualCdrom.IsoBackingInfo) + ] + if not iso_cdroms: + return { + "vm": vm_name, + "action": "no_iso_mounted", + "previous_iso": None, + "ejected": [], + } - # Create empty client device backing (ejects the ISO) - backing = vim.vm.device.VirtualCdrom.RemotePassthroughBackingInfo() - backing.deviceName = "" + device_changes = [] + ejected = [] + for cdrom in iso_cdroms: + ejected.append( + {"cdrom": cdrom.deviceInfo.label, "previous_iso": cdrom.backing.fileName} + ) + backing = vim.vm.device.VirtualCdrom.RemotePassthroughBackingInfo() + backing.deviceName = "" + backing.exclusive = False + cdrom.backing = backing + cdrom.connectable = vim.vm.device.VirtualDevice.ConnectInfo() + cdrom.connectable.connected = False + cdrom.connectable.startConnected = False + cdrom.connectable.allowGuestControl = True - # Configure CD-ROM - cdrom.backing = backing - cdrom.connectable = vim.vm.device.VirtualDevice.ConnectInfo() - cdrom.connectable.connected = False - cdrom.connectable.startConnected = False - cdrom.connectable.allowGuestControl = True + spec = vim.vm.device.VirtualDeviceSpec() + spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit + spec.device = cdrom + device_changes.append(spec) - # Create device edit spec - cdrom_spec = vim.vm.device.VirtualDeviceSpec() - cdrom_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit - cdrom_spec.device = cdrom - - # Create VM config spec - config_spec = vim.vm.ConfigSpec() - config_spec.deviceChange = [cdrom_spec] - - # Reconfigure VM + config_spec = vim.vm.ConfigSpec(deviceChange=device_changes) task = vm.ReconfigVM_Task(spec=config_spec) conn.wait_for_task(task) return { "vm": vm_name, "action": "iso_detached", - "previous_iso": old_iso, - "cdrom": cdrom.deviceInfo.label, + "previous_iso": ejected[0]["previous_iso"], + "ejected": ejected, }