diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRestoreBackupCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRestoreBackupCommandWrapper.java index ccd0ec634525..46941cceda0f 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRestoreBackupCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRestoreBackupCommandWrapper.java @@ -431,14 +431,16 @@ private boolean attachRbdVolumeToVm(KVMStoragePoolManager storagePoolMgr, String } private String getDeviceToAttachDisk(String vmName) { - 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 + // 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" }; // The commands are executed without a shell, so the awk program must be passed as a plain // argument. Keeping the quotes a shell would have stripped makes awk fail with // "invalid char" and produce no output. - String[] awkCmd = new String[] { Script.getExecutableAbsolutePath("awk"), "{print $1}" }; - Pair result = Script.executePipedCommands(Arrays.asList(domblkCmd, tailCmd, headCmd, awkCmd), 0); + String[] awkCmd = new String[] { Script.getExecutableAbsolutePath("awk"), "$2==\"disk\"{print $3}" }; + String[] tailCmd = new String[] { Script.getExecutableAbsolutePath("tail"), "-n", "1" }; + Pair result = Script.executePipedCommands(Arrays.asList(domblkCmd, awkCmd, tailCmd), 0); // executePipedCommands appends a line separator to every line it reads, so the device // name has to be trimmed before the last character can be incremented. String currentDevice = result.second() == null ? "" : result.second().trim(); diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRestoreBackupCommandWrapperTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRestoreBackupCommandWrapperTest.java index 3bdc23d27a15..b6766e79ed52 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRestoreBackupCommandWrapperTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRestoreBackupCommandWrapperTest.java @@ -30,6 +30,7 @@ import java.nio.file.Path; import java.util.Arrays; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; import org.apache.cloudstack.backup.BackupAnswer; import org.apache.cloudstack.backup.RestoreBackupCommand; @@ -655,10 +656,33 @@ public void testGetDeviceToAttachDiskPassesUnquotedAwkProgram() throws Exception invokeGetDeviceToAttachDisk("test-vm"); - String[] awkCmd = captured[0].get(captured[0].size() - 1); + String[] awkCmd = captured[0].get(1); // 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]); + } + } + + @Test + public void testGetDeviceToAttachDiskRequestsDomblklistDetails() throws Exception { + try (MockedStatic