From 905027c2a39069798b36963f38af47bff2222fd9 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Sat, 31 Oct 2026 00:30:18 +0530 Subject: [PATCH] core: fix instance snapshot failure Fix null volume list in KVM disk-only VM snapshot commands Populate the inherited volume list from the volume/path pairs so UEFI NVRAM backup can locate the root volume without throwing a NullPointerException. Update the NVRAM regression test to use a real snapshot command instead of mocking the missing list. Signed-off-by: Abhishek Kumar --- .../api/storage/CreateDiskOnlyVmSnapshotCommand.java | 4 +++- .../LibvirtDiskOnlyVMSnapshotCommandWrapperTest.java | 9 +++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotCommand.java b/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotCommand.java index cb731898a4d2..a41aed89a9d4 100644 --- a/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotCommand.java +++ b/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotCommand.java @@ -26,6 +26,7 @@ import org.apache.cloudstack.storage.to.VolumeObjectTO; import java.util.List; +import java.util.stream.Collectors; public class CreateDiskOnlyVmSnapshotCommand extends VMSnapshotBaseCommand { @@ -41,7 +42,8 @@ public CreateDiskOnlyVmSnapshotCommand(String vmName, VMSnapshotTO snapshot, Lis public CreateDiskOnlyVmSnapshotCommand(String vmName, String vmUuid, VMSnapshotTO snapshot, List> volumeTosAndNewPaths, String guestOSType, VirtualMachine.State vmState, boolean uefiEnabled) { - super(vmName, snapshot, null, guestOSType); + super(vmName, snapshot, volumeTosAndNewPaths == null ? null : volumeTosAndNewPaths.stream() + .map(Pair::first).collect(Collectors.toList()), guestOSType); this.vmUuid = vmUuid; this.vmState = vmState; this.volumeTosAndNewPaths = volumeTosAndNewPaths; diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtDiskOnlyVMSnapshotCommandWrapperTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtDiskOnlyVMSnapshotCommandWrapperTest.java index 35c8f6521fb8..c9a1e0bf08d9 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtDiskOnlyVMSnapshotCommandWrapperTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtDiskOnlyVMSnapshotCommandWrapperTest.java @@ -59,6 +59,7 @@ import com.cloud.storage.Storage; import com.cloud.storage.Volume; import com.cloud.utils.Pair; +import com.cloud.vm.VirtualMachine; public class LibvirtDiskOnlyVMSnapshotCommandWrapperTest { @@ -68,21 +69,17 @@ public void testBackupNvramIfNeededCopiesActiveNvram() throws Exception { LibvirtComputingResource resource = mock(LibvirtComputingResource.class); KVMStoragePoolManager storagePoolManager = mock(KVMStoragePoolManager.class); KVMStoragePool storagePool = mock(KVMStoragePool.class); - CreateDiskOnlyVmSnapshotCommand command = mock(CreateDiskOnlyVmSnapshotCommand.class); VMSnapshotTO vmSnapshotTO = mock(VMSnapshotTO.class); PrimaryDataStoreTO dataStoreTO = mock(PrimaryDataStoreTO.class); VolumeObjectTO rootVolume = mock(VolumeObjectTO.class); + CreateDiskOnlyVmSnapshotCommand command = new CreateDiskOnlyVmSnapshotCommand("vm-name", "vm-uuid", vmSnapshotTO, + List.of(new Pair<>(rootVolume, "snapshot-volume-path")), null, VirtualMachine.State.Running, true); Path activeNvram = Files.createTempFile("active-", ".fd"); Files.writeString(activeNvram, "snapshot-nvram"); Path poolDirectory = Files.createTempDirectory("pool-"); - when(command.isUefiEnabled()).thenReturn(true); - when(command.getVmUuid()).thenReturn("vm-uuid"); - when(command.getVmName()).thenReturn("vm-name"); - when(command.getTarget()).thenReturn(vmSnapshotTO); when(vmSnapshotTO.getId()).thenReturn(42L); - when(command.getVolumeTOs()).thenReturn(List.of(rootVolume)); when(rootVolume.getVolumeType()).thenReturn(Volume.Type.ROOT); when(rootVolume.getDataStore()).thenReturn(dataStoreTO); when(dataStoreTO.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);