Skip to content

Commit 25aa28b

Browse files
author
kunal.behbudzade
committed
KVM: capture UEFI NVRAM in disk-only instance snapshots
Copy the active UEFI NVRAM file as a sidecar during disk-only instance snapshot creation, restore it on revert, and clean it up during delete and merge flows. Also tighten host capability checks, preserve successful snapshot metadata when post-snapshot thaw or resume fails, and reject reverting UEFI disk-only snapshots that do not contain NVRAM state.
1 parent b973a38 commit 25aa28b

File tree

5 files changed

+515
-16
lines changed

5 files changed

+515
-16
lines changed

PendingReleaseNotes

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,12 @@ example.ver.1 > example.ver.2:
3939
which can now be attached to Instances. This is to prevent the Secondary
4040
Storage to grow to enormous sizes as Linux Distributions keep growing in
4141
size while a stripped down Linux should fit on a 2.88MB floppy.
42+
43+
4.22.0.0 > 4.22.0.1:
44+
* Disk-only instance snapshots for KVM UEFI VMs now include a sidecar copy of
45+
the active NVRAM state so revert operations restore both disk and firmware
46+
boot state consistently.
47+
48+
* UEFI disk-only instance snapshots taken before this change do not contain an
49+
NVRAM sidecar and cannot be safely reverted. Take a new snapshot after
50+
upgrading before relying on revert for UEFI VMs.

engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/KvmFileBasedStorageVmSnapshotStrategy.java

Lines changed: 162 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,13 @@
2828
import com.cloud.agent.api.storage.RevertDiskOnlyVmSnapshotCommand;
2929
import com.cloud.agent.api.storage.SnapshotMergeTreeTO;
3030
import com.cloud.agent.api.to.DataTO;
31+
import com.cloud.alert.AlertManager;
3132
import com.cloud.configuration.Resource;
3233
import com.cloud.event.EventTypes;
3334
import com.cloud.event.UsageEventUtils;
35+
import com.cloud.host.DetailVO;
36+
import com.cloud.host.Host;
37+
import com.cloud.host.dao.HostDetailsDao;
3438
import com.cloud.hypervisor.Hypervisor;
3539
import com.cloud.storage.DataStoreRole;
3640
import com.cloud.storage.Snapshot;
@@ -46,9 +50,11 @@
4650
import com.cloud.utils.fsm.NoTransitionException;
4751
import com.cloud.vm.UserVmVO;
4852
import com.cloud.vm.VirtualMachine;
53+
import com.cloud.vm.dao.VMInstanceDetailsDao;
4954
import com.cloud.vm.snapshot.VMSnapshot;
5055
import com.cloud.vm.snapshot.VMSnapshotDetailsVO;
5156
import com.cloud.vm.snapshot.VMSnapshotVO;
57+
import org.apache.cloudstack.api.ApiConstants;
5258
import org.apache.cloudstack.backup.BackupOfferingVO;
5359
import org.apache.cloudstack.backup.dao.BackupOfferingDao;
5460
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
@@ -59,10 +65,12 @@
5965
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
6066
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
6167
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
68+
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
6269
import org.apache.cloudstack.storage.snapshot.SnapshotObject;
6370
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
6471
import org.apache.cloudstack.storage.to.VolumeObjectTO;
6572
import org.apache.commons.collections.CollectionUtils;
73+
import org.apache.commons.lang3.StringUtils;
6674

6775
import javax.inject.Inject;
6876
import java.util.ArrayList;
@@ -76,6 +84,7 @@
7684
public class KvmFileBasedStorageVmSnapshotStrategy extends StorageVMSnapshotStrategy {
7785

7886
private static final List<Storage.StoragePoolType> supportedStoragePoolTypes = List.of(Storage.StoragePoolType.Filesystem, Storage.StoragePoolType.NetworkFilesystem, Storage.StoragePoolType.SharedMountPoint);
87+
private static final String KVM_FILE_BASED_STORAGE_SNAPSHOT_NVRAM = "kvmFileBasedStorageSnapshotNvram";
7988

8089
@Inject
8190
protected SnapshotDataStoreDao snapshotDataStoreDao;
@@ -86,6 +95,15 @@ public class KvmFileBasedStorageVmSnapshotStrategy extends StorageVMSnapshotStra
8695
@Inject
8796
protected BackupOfferingDao backupOfferingDao;
8897

98+
@Inject
99+
protected VMInstanceDetailsDao vmInstanceDetailsDao;
100+
101+
@Inject
102+
protected HostDetailsDao hostDetailsDao;
103+
104+
@Inject
105+
protected AlertManager alertManager;
106+
89107
@Override
90108
public VMSnapshot takeVMSnapshot(VMSnapshot vmSnapshot) {
91109
Map<VolumeInfo, SnapshotObject> volumeInfoToSnapshotObjectMap = new HashMap<>();
@@ -117,13 +135,15 @@ public boolean deleteVMSnapshot(VMSnapshot vmSnapshot) {
117135
UserVmVO userVm = userVmDao.findById(vmSnapshot.getVmId());
118136
VMSnapshotVO vmSnapshotBeingDeleted = (VMSnapshotVO) vmSnapshot;
119137
Long hostId = vmSnapshotHelper.pickRunningHost(vmSnapshotBeingDeleted.getVmId());
138+
validateHostSupportsNvramSidecarCleanup(vmSnapshotBeingDeleted, hostId, "delete");
120139
long virtualSize = 0;
121140
boolean isCurrent = vmSnapshotBeingDeleted.getCurrent();
122141

123142
transitStateWithoutThrow(vmSnapshotBeingDeleted, VMSnapshot.Event.ExpungeRequested);
124143

125144
List<VolumeObjectTO> volumeTOs = vmSnapshotHelper.getVolumeTOList(vmSnapshotBeingDeleted.getVmId());
126145
List<VMSnapshotVO> snapshotChildren = vmSnapshotDao.listByParentAndStateIn(vmSnapshotBeingDeleted.getId(), VMSnapshot.State.Ready, VMSnapshot.State.Hidden);
146+
PrimaryDataStoreTO nvramPrimaryDataStore = getPrimaryDataStoreForNvramCleanup(vmSnapshotBeingDeleted, volumeTOs);
127147

128148
long realSize = getVMSnapshotRealSize(vmSnapshotBeingDeleted);
129149
int numberOfChildren = snapshotChildren.size();
@@ -157,6 +177,8 @@ public boolean deleteVMSnapshot(VMSnapshot vmSnapshot) {
157177
return true;
158178
}
159179

180+
deleteNvramSnapshotIfNeeded(vmSnapshotBeingDeleted, hostId, nvramPrimaryDataStore);
181+
160182
transitStateWithoutThrow(vmSnapshotBeingDeleted, VMSnapshot.Event.OperationSucceeded);
161183

162184
vmSnapshotDetailsDao.removeDetails(vmSnapshotBeingDeleted.getId());
@@ -176,6 +198,7 @@ public boolean revertVMSnapshot(VMSnapshot vmSnapshot) {
176198

177199
VMSnapshotVO vmSnapshotBeingReverted = (VMSnapshotVO) vmSnapshot;
178200
Long hostId = vmSnapshotHelper.pickRunningHost(vmSnapshotBeingReverted.getVmId());
201+
validateHostSupportsUefiNvramAwareDiskOnlySnapshots(hostId, userVm, "revert");
179202

180203
transitStateWithoutThrow(vmSnapshotBeingReverted, VMSnapshot.Event.RevertRequested);
181204

@@ -184,7 +207,9 @@ public boolean revertVMSnapshot(VMSnapshot vmSnapshot) {
184207
.map(snapshot -> (SnapshotObjectTO) snapshotDataFactory.getSnapshot(snapshot.getSnapshotId(), snapshot.getDataStoreId(), DataStoreRole.Primary).getTO())
185208
.collect(Collectors.toList());
186209

187-
RevertDiskOnlyVmSnapshotCommand revertDiskOnlyVMSnapshotCommand = new RevertDiskOnlyVmSnapshotCommand(volumeSnapshotTos, userVm.getName());
210+
RevertDiskOnlyVmSnapshotCommand revertDiskOnlyVMSnapshotCommand =
211+
new RevertDiskOnlyVmSnapshotCommand(volumeSnapshotTos, userVm.getName(), userVm.getUuid(), isUefiVm(userVm),
212+
getNvramSnapshotPath(vmSnapshotBeingReverted));
188213
Answer answer = agentMgr.easySend(hostId, revertDiskOnlyVMSnapshotCommand);
189214

190215
if (answer == null || !answer.getResult()) {
@@ -204,6 +229,11 @@ public boolean revertVMSnapshot(VMSnapshot vmSnapshot) {
204229
publishUsageEvent(EventTypes.EVENT_VM_SNAPSHOT_REVERT, vmSnapshotBeingReverted, userVm, volumeObjectTo);
205230
}
206231

232+
if (isUefiVm(userVm)) {
233+
userVm.setLastHostId(hostId);
234+
userVmDao.update(userVm.getId(), userVm);
235+
}
236+
207237
transitStateWithoutThrow(vmSnapshotBeingReverted, VMSnapshot.Event.OperationSucceeded);
208238

209239
VMSnapshotVO currentVmSnapshot = vmSnapshotDao.findCurrentSnapshotByVmId(userVm.getId());
@@ -248,6 +278,8 @@ private void mergeOldSiblingWithOldParentIfOldParentIsDead(VMSnapshotVO oldParen
248278
return;
249279
}
250280

281+
validateHostSupportsNvramSidecarCleanup(oldParent, hostId, "clean up");
282+
PrimaryDataStoreTO nvramPrimaryDataStore = getPrimaryDataStoreForNvramCleanup(oldParent, volumeTOs);
251283
List<SnapshotVO> snapshotVos;
252284

253285
if (oldParent.getCurrent()) {
@@ -276,6 +308,8 @@ private void mergeOldSiblingWithOldParentIfOldParentIsDead(VMSnapshotVO oldParen
276308
snapshotDao.update(snapshotVO.getId(), snapshotVO);
277309
}
278310

311+
deleteNvramSnapshotIfNeeded(oldParent, hostId, nvramPrimaryDataStore);
312+
279313
vmSnapshotDetailsDao.removeDetails(oldParent.getId());
280314

281315
oldParent.setRemoved(DateUtil.now());
@@ -347,12 +381,13 @@ public StrategyPriority canHandle(Long vmId, Long rootPoolId, boolean snapshotMe
347381
}
348382

349383
private List<SnapshotVO> deleteSnapshot(VMSnapshotVO vmSnapshotVO, Long hostId) {
384+
validateHostSupportsNvramSidecarCleanup(vmSnapshotVO, hostId, "delete");
350385
List<SnapshotDataStoreVO> volumeSnapshots = getVolumeSnapshotsAssociatedWithVmSnapshot(vmSnapshotVO);
351386
List<DataTO> volumeSnapshotTOList = volumeSnapshots.stream()
352387
.map(snapshotDataStoreVO -> snapshotDataFactory.getSnapshot(snapshotDataStoreVO.getSnapshotId(), snapshotDataStoreVO.getDataStoreId(), DataStoreRole.Primary).getTO())
353388
.collect(Collectors.toList());
354389

355-
DeleteDiskOnlyVmSnapshotCommand deleteSnapshotCommand = new DeleteDiskOnlyVmSnapshotCommand(volumeSnapshotTOList);
390+
DeleteDiskOnlyVmSnapshotCommand deleteSnapshotCommand = new DeleteDiskOnlyVmSnapshotCommand(volumeSnapshotTOList, getNvramSnapshotPath(vmSnapshotVO));
356391
Answer answer = agentMgr.easySend(hostId, deleteSnapshotCommand);
357392
if (answer == null || !answer.getResult()) {
358393
logger.error("Failed to delete VM snapshot [{}] due to {}.", vmSnapshotVO.getUuid(), answer != null ? answer.getDetails() : "Communication failure");
@@ -368,6 +403,21 @@ private List<SnapshotVO> deleteSnapshot(VMSnapshotVO vmSnapshotVO, Long hostId)
368403
return snapshotVOList;
369404
}
370405

406+
protected void deleteNvramSnapshotIfNeeded(VMSnapshotVO vmSnapshotVO, Long hostId, PrimaryDataStoreTO primaryDataStore) {
407+
String nvramSnapshotPath = getNvramSnapshotPath(vmSnapshotVO);
408+
if (StringUtils.isBlank(nvramSnapshotPath) || primaryDataStore == null) {
409+
return;
410+
}
411+
412+
validateHostSupportsNvramSidecarCleanup(vmSnapshotVO, hostId, "delete");
413+
DeleteDiskOnlyVmSnapshotCommand deleteSnapshotCommand = new DeleteDiskOnlyVmSnapshotCommand(List.of(), nvramSnapshotPath, primaryDataStore);
414+
Answer answer = agentMgr.easySend(hostId, deleteSnapshotCommand);
415+
if (answer == null || !answer.getResult()) {
416+
logger.warn("Failed to delete the NVRAM sidecar of VM snapshot [{}] due to {}.", vmSnapshotVO.getUuid(),
417+
answer != null ? answer.getDetails() : "communication failure");
418+
}
419+
}
420+
371421
private List<SnapshotVO> mergeSnapshots(VMSnapshotVO vmSnapshotVO, VMSnapshotVO childSnapshot, UserVmVO userVm, List<VolumeObjectTO> volumeObjectTOS, Long hostId) {
372422
logger.debug("Merging VM snapshot [{}] with its child [{}].", vmSnapshotVO.getUuid(), childSnapshot.getUuid());
373423

@@ -471,6 +521,7 @@ protected VMSnapshot takeVmSnapshotInternal(VMSnapshot vmSnapshot, Map<VolumeInf
471521
logger.info("Starting disk-only VM snapshot process for VM [{}].", userVm.getUuid());
472522

473523
Long hostId = vmSnapshotHelper.pickRunningHost(vmSnapshot.getVmId());
524+
validateHostSupportsUefiNvramAwareDiskOnlySnapshots(hostId, userVm, "create");
474525
VMSnapshotVO vmSnapshotVO = (VMSnapshotVO) vmSnapshot;
475526
List<VolumeObjectTO> volumeTOs = vmSnapshotHelper.getVolumeTOList(userVm.getId());
476527

@@ -493,14 +544,18 @@ protected VMSnapshot takeVmSnapshotInternal(VMSnapshot vmSnapshot, Map<VolumeInf
493544

494545
VMSnapshotTO target = new VMSnapshotTO(vmSnapshot.getId(), vmSnapshot.getName(), vmSnapshot.getType(), null, vmSnapshot.getDescription(), false, parentSnapshotTo, quiesceVm);
495546

496-
CreateDiskOnlyVmSnapshotCommand ccmd = new CreateDiskOnlyVmSnapshotCommand(userVm.getInstanceName(), target, volumeTOs, null, userVm.getState());
547+
CreateDiskOnlyVmSnapshotCommand ccmd =
548+
new CreateDiskOnlyVmSnapshotCommand(userVm.getInstanceName(), userVm.getUuid(), target, volumeTOs, null, userVm.getState(), isUefiVm(userVm));
497549

498550
logger.info("Sending disk-only VM snapshot creation of VM Snapshot [{}] command for host [{}].", vmSnapshot.getUuid(), hostId);
499551
Answer answer = agentMgr.easySend(hostId, ccmd);
500552

501553
if (answer != null && answer.getResult()) {
502554
CreateDiskOnlyVmSnapshotAnswer createDiskOnlyVMSnapshotAnswer = (CreateDiskOnlyVmSnapshotAnswer) answer;
503-
return processCreateVmSnapshotAnswer(vmSnapshot, volumeInfoToSnapshotObjectMap, createDiskOnlyVMSnapshotAnswer, userVm, vmSnapshotVO, virtualSize, parentSnapshotVo);
555+
VMSnapshot createdVmSnapshot = processCreateVmSnapshotAnswer(vmSnapshot, volumeInfoToSnapshotObjectMap, createDiskOnlyVMSnapshotAnswer, userVm, vmSnapshotVO,
556+
virtualSize, parentSnapshotVo);
557+
notifyGuestRecoveryIssueIfNeeded(createDiskOnlyVMSnapshotAnswer, userVm, vmSnapshotVO);
558+
return createdVmSnapshot;
504559
}
505560

506561
logger.error("Disk-only VM snapshot for VM [{}] failed{}.", userVm.getUuid(), answer != null ? " due to" + answer.getDetails() : "");
@@ -541,6 +596,14 @@ private VMSnapshot processCreateVmSnapshotAnswer(VMSnapshot vmSnapshot, Map<Volu
541596
publishUsageEvent(EventTypes.EVENT_VM_SNAPSHOT_CREATE, vmSnapshot, userVm, (VolumeObjectTO) volumeInfo.getTO());
542597
}
543598

599+
if (StringUtils.isNotBlank(answer.getNvramSnapshotPath())) {
600+
vmSnapshotDetailsDao.addDetail(vmSnapshot.getId(), KVM_FILE_BASED_STORAGE_SNAPSHOT_NVRAM, answer.getNvramSnapshotPath(), false);
601+
} else if (isUefiVm(userVm)) {
602+
logger.warn("Disk-only snapshot [{}] for UEFI VM [{}] was created without an NVRAM sidecar and cannot be safely reverted. "
603+
+ "Upgrade the KVM agent and take a new snapshot.",
604+
vmSnapshot.getUuid(), userVm.getUuid());
605+
}
606+
544607
vmSnapshotVO.setCurrent(true);
545608
vmSnapshotDao.persist(vmSnapshotVO);
546609

@@ -651,6 +714,101 @@ private long getVMSnapshotRealSize(VMSnapshotVO vmSnapshot) {
651714
return realSize;
652715
}
653716

717+
protected boolean isUefiVm(UserVm userVm) {
718+
return vmInstanceDetailsDao.findDetail(userVm.getId(), ApiConstants.BootType.UEFI.toString()) != null;
719+
}
720+
721+
protected PrimaryDataStoreTO getRootVolumePrimaryDataStore(List<VolumeObjectTO> volumeTOs) {
722+
return (PrimaryDataStoreTO) volumeTOs.stream()
723+
.filter(volumeObjectTO -> Volume.Type.ROOT.equals(volumeObjectTO.getVolumeType()))
724+
.findFirst()
725+
.orElseThrow(() -> new CloudRuntimeException("Failed to locate the root volume while handling the VM snapshot."))
726+
.getDataStore();
727+
}
728+
729+
protected PrimaryDataStoreTO getRootVolumePrimaryDataStoreForCleanup(VMSnapshotVO vmSnapshot, List<VolumeObjectTO> volumeTOs) {
730+
try {
731+
return getRootVolumePrimaryDataStore(volumeTOs);
732+
} catch (CloudRuntimeException e) {
733+
logger.warn("Failed to locate the root volume while cleaning up the NVRAM sidecar for VM snapshot [{}].", vmSnapshot.getUuid(), e);
734+
return null;
735+
}
736+
}
737+
738+
protected PrimaryDataStoreTO getPrimaryDataStoreForNvramCleanup(VMSnapshotVO vmSnapshot, List<VolumeObjectTO> volumeTOs) {
739+
PrimaryDataStoreTO rootSnapshotPrimaryDataStore = getRootSnapshotPrimaryDataStoreForCleanup(vmSnapshot);
740+
return rootSnapshotPrimaryDataStore != null ? rootSnapshotPrimaryDataStore : getRootVolumePrimaryDataStoreForCleanup(vmSnapshot, volumeTOs);
741+
}
742+
743+
protected PrimaryDataStoreTO getRootSnapshotPrimaryDataStoreForCleanup(VMSnapshotVO vmSnapshot) {
744+
try {
745+
return (PrimaryDataStoreTO) getVolumeSnapshotsAssociatedWithVmSnapshot(vmSnapshot).stream()
746+
.map(snapshotDataStoreVO -> (SnapshotObjectTO) snapshotDataFactory.getSnapshot(snapshotDataStoreVO.getSnapshotId(),
747+
snapshotDataStoreVO.getDataStoreId(), DataStoreRole.Primary).getTO())
748+
.filter(snapshotObjectTO -> Volume.Type.ROOT.equals(snapshotObjectTO.getVolume().getVolumeType()))
749+
.findFirst()
750+
.orElseThrow(() -> new CloudRuntimeException("Failed to locate the root volume snapshot while handling the VM snapshot."))
751+
.getDataStore();
752+
} catch (CloudRuntimeException e) {
753+
logger.warn("Failed to locate the root volume snapshot while cleaning up the NVRAM sidecar for VM snapshot [{}].", vmSnapshot.getUuid(), e);
754+
return null;
755+
}
756+
}
757+
758+
protected String getNvramSnapshotPath(VMSnapshotVO vmSnapshot) {
759+
VMSnapshotDetailsVO nvramDetail = vmSnapshotDetailsDao.findDetail(vmSnapshot.getId(), KVM_FILE_BASED_STORAGE_SNAPSHOT_NVRAM);
760+
return nvramDetail != null ? nvramDetail.getValue() : null;
761+
}
762+
763+
protected void validateHostSupportsUefiNvramAwareDiskOnlySnapshots(Long hostId, UserVm userVm, String operation) {
764+
if (!isUefiVm(userVm)) {
765+
return;
766+
}
767+
768+
if (!isHostCapabilityEnabled(hostId, Host.HOST_UEFI_ENABLE)) {
769+
throw new CloudRuntimeException(String.format("Cannot %s a disk-only snapshot for UEFI VM [%s] on host [%s] because the host does not advertise "
770+
+ "UEFI support. Ensure the host is configured with UEFI support and retry.", operation, userVm.getUuid(), hostId));
771+
}
772+
773+
if (!isHostCapabilityEnabled(hostId, Host.HOST_KVM_DISK_ONLY_VM_SNAPSHOT_NVRAM)) {
774+
throw new CloudRuntimeException(String.format("Cannot %s a disk-only snapshot for UEFI VM [%s] on host [%s] because the KVM agent does not advertise "
775+
+ "NVRAM-aware disk-only snapshot support. Upgrade the host and retry.", operation, userVm.getUuid(), hostId));
776+
}
777+
}
778+
779+
protected boolean isHostCapabilityEnabled(Long hostId, String capabilityName) {
780+
DetailVO hostCapability = hostDetailsDao.findDetail(hostId, capabilityName);
781+
return hostCapability != null && Boolean.parseBoolean(hostCapability.getValue());
782+
}
783+
784+
protected void validateHostSupportsNvramSidecarCleanup(VMSnapshotVO vmSnapshotVO, Long hostId, String operation) {
785+
if (StringUtils.isBlank(getNvramSnapshotPath(vmSnapshotVO))) {
786+
return;
787+
}
788+
789+
if (!isHostCapabilityEnabled(hostId, Host.HOST_KVM_DISK_ONLY_VM_SNAPSHOT_NVRAM)) {
790+
throw new CloudRuntimeException(String.format("Cannot %s VM snapshot [%s] on host [%s] because the KVM agent does not advertise "
791+
+ "NVRAM-aware disk-only snapshot support and the snapshot has an NVRAM sidecar that must be cleaned up. Upgrade the host and retry.",
792+
operation, vmSnapshotVO.getUuid(), hostId));
793+
}
794+
}
795+
796+
protected void notifyGuestRecoveryIssueIfNeeded(CreateDiskOnlyVmSnapshotAnswer answer, UserVm userVm, VMSnapshotVO vmSnapshot) {
797+
if (StringUtils.isBlank(answer.getDetails())) {
798+
return;
799+
}
800+
801+
String subject = String.format("Disk-only VM snapshot [%s] completed with guest recovery warnings", vmSnapshot.getUuid());
802+
String message = String.format("Disk-only VM snapshot [%s] for UEFI VM [%s] completed, but post-snapshot guest recovery reported: %s",
803+
vmSnapshot.getUuid(), userVm.getUuid(), answer.getDetails());
804+
logger.error(message);
805+
try {
806+
alertManager.sendAlert(AlertManager.AlertType.ALERT_TYPE_VM_SNAPSHOT, userVm.getDataCenterId(), userVm.getPodIdToDeployIn(), subject, message);
807+
} catch (Exception e) {
808+
logger.warn("Failed to send post-snapshot guest recovery alert for VM snapshot [{}].", vmSnapshot.getUuid(), e);
809+
}
810+
}
811+
654812
/**
655813
* Given a list of VM snapshots, will remove any that are part of the current direct backing chain (all the direct ancestors of the current vm snapshot).
656814
* This is done because, when using <a href="https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainBlockCommit">virDomainBlockCommit</a>}, Libvirt will maintain

0 commit comments

Comments
 (0)