Introduction:

CloudStack currently supports at most one ISO per VM. This forces users into a manual "ISO swap" workflow for common tasks such as installing Windows on KVM — which requires both a Windows installer ISO and a VirtIO drivers ISO simultaneously — and pushes template creation to external tools like Proxmox or virt-manager. This feature adds native support for attaching multiple ISOs per VM, each surfaced as a discrete CD-ROM drive inside the guest. The Windows + VirtIO use case is a natural consequence of the generic multi-ISO capability.

Note: KVM only for 4.23.

Scope:

  • KVM hypervisor only.
  • New cluster-scoped config vm.iso.max.count to cap the number of CD-ROMs per VM. Per-cluster overrides are supported.
  • Multi-ISO state persists across stop/start cycles.
  • UI: action menu and modal dialogs become multi-ISO aware; instance detail page lists all attached ISOs with their slot labels.

API Changes:

attachIso

No new parameters. Behavior changes:

  • Was: if the VM already has an ISO attached, the new attach replaces it.
  • Now: each call adds an ISO up to the effective cap. The first attach lands in user_vm.iso_id (the bootable/primary slot); subsequent attaches create a new vm_iso_map row.
  • Rejects if the same ISO is already attached to the VM.
  • Rejects with a clear error message when the effective cap is reached.

detachIso

New optional parameter:

  • id (UUID, optional; annotated since=4.23.0) — the ISO to detach.

Rules:

  • If only one ISO is attached, id may be omitted (back-compat).
  • If multiple ISOs are attached, id is required; the call is rejected without it.
  • If id is supplied and the specified ISO is not attached, the call is rejected.

listVirtualMachines

Response gains a new isos[] array. Each entry contains:

  • id, name, displaytext
  • deviceseq — the CD-ROM slot index (3 = hdc, 4 = hdd, …)

The legacy isoid / isoname / isodisplaytext fields are unchanged and continue to reflect the slot-3 (bootable/primary) ISO. Existing API consumers require no modification.

UserVmResponse now also exposes cdrommaxcount (since 4.23.0) — the server-computed effective cap for the VM. The UI reads this field directly from the response instead of making a separate listConfigurations call.

Configuration / Cap Derivation:

ParameterScopeTypeDefaultDescription
vm.iso.max.countClusterInteger1Maximum number of CD-ROM drives (ISOs) that may be attached to a single VM. Enforced at attach and deploy time. Per-cluster overrides are supported.

The effective max for a given VM is: min(vm.iso.max.count.valueIn(clusterId), host-advertised hypervisor cap). The hardcoded "2 for KVM, 1 for others" logic has been removed from the management server; the hypervisor cap is now discovered at runtime (see Hypervisor Cap Discovery below).

CloudMonkey (cmk) Examples:

1. attachIso — attach two ISOs back-to-back to a running VM

cmk attach iso id=<windows-iso-uuid> virtualmachineid=<vm-uuid>
cmk attach iso id=<virtio-iso-uuid>  virtualmachineid=<vm-uuid>

2. detachIso — single ISO attached (id omitted)

cmk detach iso virtualmachineid=<vm-uuid>

3. detachIso — multiple ISOs attached (id required)

cmk detach iso id=<virtio-iso-uuid> virtualmachineid=<vm-uuid>

4. listVirtualMachines — isos[] in response

{
  "id": "a1b2c3d4-...",
  "name": "my-vm",
  "isoid": "<windows-iso-uuid>",
  "isoname": "Windows Server 2022",
  "isos": [
    {
      "id": "<windows-iso-uuid>",
      "name": "Windows Server 2022",
      "displaytext": "Windows Server 2022",
      "deviceseq": 3
    },
    {
      "id": "<virtio-iso-uuid>",
      "name": "virtio-win-0.9.x",
      "displaytext": "VirtIO Drivers",
      "deviceseq": 4
    }
  ],
  ...
}

Behavior Notes:

Hypervisor Cap Discovery:

Each hypervisor agent advertises its own CD-ROM cap as a host detail named host.cdrom.max.count (Host.HOST_CDROM_MAX_COUNT) on StartupRoutingCommand. The management server reads this via HostDetailsDao and falls back to TemplateManager.DEFAULT_CDROM_MAX_PER_VM (= 1) if the detail is absent (older agents, never-deployed VMs).

  • The KVM agent advertises 2 (LibvirtVMDef.MAX_CDROMS_PER_VM), co-located with the slot-allocation logic in getDevLabel that places CD-ROMs at hdc (TemplateManager.CDROM_PRIMARY_DEVICE_SEQ = 3) and hdd.
  • libvirt itself does not expose a CD-ROM slot count as a capability — domcapabilities lists supported buses but not slot counts. The number is a function of CloudStack's own slot-allocation choices, not a hypervisor-discoverable fact.
  • The effective cap for a given VM = min(vm.iso.max.count.valueIn(clusterId), host-advertised cap).

Misconfiguration Handling:

If vm.iso.max.count is set above the host-advertised cap, attach and deploy operations now log an error and throw InvalidParameterValueException with the recommended maximum in the message. The old behavior of silently clamping to the hypervisor cap has been removed.

The listVirtualMachines endpoint silently clamps the displayed cdrommaxcount value for robustness; the loud failure is reserved for action paths (attach, deploy).

Primary Slot Preservation:

The first attached ISO always occupies user_vm.iso_id (slot 3, hdc). This field is never reassigned by a subsequent attach or detach of a non-primary ISO. Detaching the primary ISO when additional ISOs remain attached is rejected; the primary slot must be the last one cleared.

Stop/Start Persistence:

All attached ISOs and their slot assignments are re-applied on VM start using data from vm_iso_map. No guest-side state is required.

Validation:

  • ISO already attached: Error "ISO <uuid> is already attached to VM <uuid>"
  • Cap exceeded: Error "The maximum number of CD-ROMs (<n>) for this VM has been reached"
  • id omitted with multiple ISOs attached: Error "Multiple ISOs are attached; the 'id' parameter is required to detach"
  • id supplied but ISO not attached: Error "ISO <uuid> is not attached to VM <uuid>"

ISO Deletion Gating:

templateIsDeleteable now also consults the vm_iso_map table (via VmIsoMapDao.listByIsoId), filtering out VMs in Error or Expunging states. Previously only user_vm.iso_id was checked, which meant an ISO attached to a non-primary slot (i.e. in vm_iso_map only) could be wrongly considered deletable even while still in use.

Constants:

ConstantLocationValue / Role
Host.HOST_CDROM_MAX_COUNTManagement serverKey for the host detail ("host.cdrom.max.count") that agents write on startup.
LibvirtVMDef.MAX_CDROMS_PER_VMKVM agentValue advertised by the KVM agent (= 2). Co-located with the getDevLabel slot-allocation logic.
TemplateManager.CDROM_PRIMARY_DEVICE_SEQManagement serverDevice sequence index of the primary (bootable) CD-ROM slot (= 3, i.e. hdc).
TemplateManager.DEFAULT_CDROM_MAX_PER_VMManagement serverFallback cap (= 1) used when the host has not advertised host.cdrom.max.count (older agents, never-deployed VMs).

Database Changes:

A new mapping table tracks secondary ISO attachments:

CREATE TABLE vm_iso_map (
  id               bigint unsigned NOT NULL AUTO_INCREMENT,
  vm_id            bigint unsigned NOT NULL COMMENT 'virtual machine id',
  iso_id           bigint unsigned NOT NULL COMMENT 'iso (volume) id',
  device_seq       int unsigned    NOT NULL COMMENT 'cdrom slot index (3=hdc, 4=hdd, ...)',
  PRIMARY KEY (id),
  CONSTRAINT fk_vm_iso_map__vm_id
    FOREIGN KEY (vm_id)  REFERENCES vm_instance(id) ON DELETE CASCADE,
  CONSTRAINT fk_vm_iso_map__iso_id
    FOREIGN KEY (iso_id) REFERENCES volumes(id)      ON DELETE CASCADE,
  UNIQUE KEY uk_vm_iso_map__vm_device (vm_id, device_seq),
  INDEX i_vm_iso_map__vm_id (vm_id),
  INDEX i_vm_iso_map__iso_id (iso_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  • Normalized design; no comma-separated ISO lists in existing columns.
  • user_vm.iso_id continues to own slot 3 (primary/bootable); vm_iso_map owns slots 4+.
  • UNIQUE KEY on (vm_id, device_seq) prevents slot collisions at the DB layer.
  • CASCADE deletes clean up map rows when a VM or volume is removed.

UI Changes:

Screen Recording 2026-05-04 at 5.20.18 PM.mp4

  • Attach ISO: existing action and modal remain. When the VM is below cap, the modal adds an ISO without replacing the current one.
  • Detach ISO: if multiple ISOs are attached, the action presents a selection list; single-ISO behavior is unchanged.
  • Instance detail page: the ISO field is replaced by an ISO list showing each attached ISO's name and slot label (e.g., hdc, hdd).
  • No labels