Conversation
getDeviceToAttachDisk found the next free disk device by piping `virsh domblklist` through `tail -n 3 | head -n 1`, just grabbing a row by position without checking what device it actually was. domblklist sorts targets alphabetically, so a VM's empty IDE cdrom slots can sort after the real disks and get picked up instead — e.g. selecting hdc and incrementing it to hdd, an already-existing IDE target, instead of the next free virtio disk. This made hot-attaching a restored volume to a running VM fail with virsh attach-disk erroring out on the bogus target. This surfaced after apache#13101 ("Multiple CD-ROM / ISO Support Per VM"), which pre-allocates a second empty cdrom slot (hdc, hdd) at boot for every VM. With two cdrom rows now sorting ahead of the VM's disks, the tail/head window shifted enough to land on a cdrom row instead of a real disk — before that change, a single cdrom slot didn't push the window that far. Add --details to domblklist and filter to rows with Type=="disk" before taking the last one, so cdrom slots are excluded and the device name is always derived from an actual disk.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #14266 +/- ##
=========================================
Coverage 19.91% 19.91%
- Complexity 20194 20200 +6
=========================================
Files 6373 6373
Lines 577230 577229 -1
Branches 70696 70696
=========================================
+ Hits 114942 114960 +18
+ Misses 449722 449700 -22
- Partials 12566 12569 +3
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Copilot review overview
Review effort: Lite
Findings: 1
Open (3)
The comment claims CD-ROM slots “sort after the real disks”, but the PR description/root cause… · New Using an array-of-generic (new List[1]) as a mutable holder is not type-safe and typically… · New This test name suggests it verifies CD-ROM entries are filtered out, but the assertions only check… · New
What changed in this PR
Fixes KVM restore+hot-attach by ensuring the next target device name is computed from actual disks (excluding CD-ROM slots) when parsing virsh domblklist output.
Changes:
- Update
getDeviceToAttachDisk()to callvirsh domblklist --detailsand filter rows whereDevice == disk. - Adjust existing unit test to match the updated piped command ordering and awk program.
- Add a unit test asserting
--detailsis requested.
| File | Description |
|---|---|
| plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRestoreBackupCommandWrapper.java | Fixes device selection logic by filtering domblklist output to disk devices only. |
| plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRestoreBackupCommandWrapperTest.java | Updates/extends tests to validate the new domblklist --details + awk filtering behavior. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Copilot review overview
Review effort: Lite
Findings: 2
Open (3)
virsh domblklist --detailsexposes a Type column, but the awk filter is currently checking… · Newvirsh domblklist --detailsexposes a Type column, but the awk filter is currently checking… · New This test locks in the same incorrect awk field index as the production code. Once the awk program… · New
Resolved since last review (3)
| // --details adds a Type column so cdrom slots (hdc, hdd) can be filtered out — they sort | ||
| // alphabetically ahead of virtio disks, so without this the selected row is a cdrom, not | ||
| // an actual disk. | ||
| String[] domblkCmd = new String[] { Script.getExecutableAbsolutePath("virsh"), "domblklist", "--domain", vmName, "--details" }; |
| // "invalid char" and produce no output. | ||
| String[] awkCmd = new String[] { Script.getExecutableAbsolutePath("awk"), "{print $1}" }; | ||
| Pair<Integer, String> result = Script.executePipedCommands(Arrays.asList(domblkCmd, tailCmd, headCmd, awkCmd), 0); | ||
| String[] awkCmd = new String[] { Script.getExecutableAbsolutePath("awk"), "$2==\"disk\"{print $3}" }; |
| // The commands are executed without a shell, so the program must carry no shell quotes. | ||
| Assert.assertEquals("awk", awkCmd[0]); | ||
| Assert.assertEquals("{print $1}", awkCmd[1]); | ||
| Assert.assertEquals("$2==\"disk\"{print $3}", awkCmd[1]); |
| String[] domblkCmd = new String[] { Script.getExecutableAbsolutePath("virsh"), "domblklist", "--domain", vmName }; | ||
| String[] tailCmd = new String[] { Script.getExecutableAbsolutePath("tail"), "-n", "3" }; | ||
| String[] headCmd = new String[] { Script.getExecutableAbsolutePath("head"), "-n", "1" }; | ||
| // --details adds a Type column so cdrom slots (hdc, hdd) can be filtered out — they sort |
There was a problem hiding this comment.
The comment here is not right. --details doesn't change the ordering in any way:
[root@ref-trl-12455-k-Mr9-abhisar-sinha-kvm1 ~]# virsh domblklist --domain i-2-5-VM --details
Type Device Target Source
----------------------------------------------------------------------------------------------------------
file disk vda /mnt/eaae5267-a60a-369d-a4da-a13db16e3aa2/250bd073-3a2c-4875-9ba0-29bf632801b7
file disk vdb /mnt/eaae5267-a60a-369d-a4da-a13db16e3aa2/6f259e43-74a5-461e-b857-e945bcff7b45
file disk vdc /mnt/eaae5267-a60a-369d-a4da-a13db16e3aa2/bca15b84-3acf-4b1d-8466-3866e563c21f
file cdrom hdc -
file cdrom hdd -
[root@ref-trl-12455-k-Mr9-abhisar-sinha-kvm1 ~]# virsh domblklist --domain i-2-5-VM
Target Source
------------------------------------------------------------------------------------------
vda /mnt/eaae5267-a60a-369d-a4da-a13db16e3aa2/250bd073-3a2c-4875-9ba0-29bf632801b7
vdb /mnt/eaae5267-a60a-369d-a4da-a13db16e3aa2/6f259e43-74a5-461e-b857-e945bcff7b45
vdc /mnt/eaae5267-a60a-369d-a4da-a13db16e3aa2/bca15b84-3acf-4b1d-8466-3866e563c21f
hdc -
hdd -
[root@ref-trl-12455-k-Mr9-abhisar-sinha-kvm1 ~]#
--details does add the type Device column (not Type) which awk $2==\"disk\" uses to filter by type.



Description
Problem
Restoring a single volume from backup and hot-attaching it to a running KVM VM (
restoreVolumeFromBackupAndAttachToVM) fails, regardless of the volume's storage type (NFS, StorPool, RBD, LINSTOR).####Root cause
getDeviceToAttachDisk()picks the target device name via:virsh domblklist --domain <vm> | tail -n 3 | head -n 1 | awk '{print $1}'This returns the third-from-last line, not the last one — it "works" only by coincidence. Every CloudStack KVM VM has two pre-allocated, empty CD-ROM drives (
hdc,hdd, added by #13101) that sort alphabetically ahead of the real (virtio) disks, so the pipeline picks up a CD-ROM (or an already-attached disk, depending on disk count) instead of computing a truly free name.virsh attach-diskthen rejects the bogus/duplicate target.Fix
Add
--detailstodomblklistand filter toType == "disk"rows before picking the last one, so CD-ROMs are never selected.Types of changes
Bug Severity
Screenshots (if appropriate):
How Has This Been Tested?
Manually tested with NFS and StorPool as a primary storage