Skip to content

Commit 51db06b

Browse files
committed
address review comments
1 parent 489bb3a commit 51db06b

6 files changed

Lines changed: 76 additions & 8 deletions

File tree

‎engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDao.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ public interface SnapshotDao extends GenericDao<SnapshotVO, Long>, StateDao<Snap
4545

4646
List<SnapshotVO> listByStatus(long volumeId, Snapshot.State... status);
4747

48+
List<SnapshotVO> listByStatus(Filter filter, long volumeId, Snapshot.State... status);
49+
4850
List<SnapshotVO> listAllByStatus(Snapshot.State... status);
4951

5052
List<SnapshotVO> listAllByStatusIncludingRemoved(Snapshot.State... status);

‎engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDaoImpl.java‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,10 +255,15 @@ public List<SnapshotVO> listByInstanceId(long instanceId, Snapshot.State... stat
255255

256256
@Override
257257
public List<SnapshotVO> listByStatus(long volumeId, Snapshot.State... status) {
258+
return listByStatus(null, volumeId, status);
259+
}
260+
261+
@Override
262+
public List<SnapshotVO> listByStatus(Filter filter, long volumeId, Snapshot.State... status) {
258263
SearchCriteria<SnapshotVO> sc = StatusSearch.create();
259264
sc.setParameters(VOLUME_ID, volumeId);
260265
sc.setParameters(STATUS, (Object[])status);
261-
return listBy(sc, null);
266+
return listBy(sc, filter);
262267
}
263268

264269
@Override

‎engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,3 +651,6 @@ WHERE `name`='user.vm.readonly.details' AND `value` IS NOT NULL;
651651
-- usage records introduced in 4.22.1 (cumulative and per-VM) can coexist. See #13399.
652652
CALL `cloud_usage`.`IDEMPOTENT_DROP_INDEX`('id', 'cloud_usage.usage_volume');
653653
CALL `cloud_usage`.`IDEMPOTENT_ADD_UNIQUE_INDEX`('cloud_usage.usage_volume', 'id', '(volume_id ASC, created ASC, vm_id ASC)');
654+
655+
-- Index event lookups by resource, e.g. the latest snapshot outcome events of a volume
656+
CALL `cloud`.`IDEMPOTENT_ADD_KEY`('i_event__resource_id__resource_type__type', 'cloud.event', '(`resource_id`, `resource_type`, `type`, `created`)');

‎server/src/main/java/com/cloud/storage/snapshot/SnapshotManager.java‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,15 @@ public interface SnapshotManager extends Configurable {
5757

5858
ConfigKey<Integer> SnapshotRecurringMaxFailures = new ConfigKey<>(Integer.class, "snapshot.recurring.max.failures", "Snapshots", "3",
5959
"Maximum number of consecutive failed attempts allowed for a recurring snapshot before it is left until its next regularly scheduled run and a failure event is logged. Set to 0 to retry indefinitely.",
60-
true, List.of(ConfigKey.Scope.Account, ConfigKey.Scope.Domain, ConfigKey.Scope.Zone, ConfigKey.Scope.Global), null);
60+
true, List.of(ConfigKey.Scope.Account, ConfigKey.Scope.Domain, ConfigKey.Scope.Zone), null);
6161

6262
ConfigKey<Integer> SnapshotRecurringRetryInterval = new ConfigKey<>(Integer.class, "snapshot.recurring.retry.interval", "Snapshots", "300",
6363
"Time in seconds to wait before retrying a failed recurring snapshot attempt.",
64-
true, List.of(ConfigKey.Scope.Account, ConfigKey.Scope.Domain, ConfigKey.Scope.Zone, ConfigKey.Scope.Global), null);
64+
true, List.of(ConfigKey.Scope.Account, ConfigKey.Scope.Domain, ConfigKey.Scope.Zone), null);
6565

6666
ConfigKey<Boolean> SnapshotSkipIfVmNotRunning = new ConfigKey<>(Boolean.class, "snapshot.skip.if.vm.not.running", "Snapshots", "false",
6767
"For recurring snapshots, skip taking a new snapshot of a volume (and log a skipped event instead) when the VM it is attached to has not been running since the last snapshot was taken, since nothing on the volume could have changed.",
68-
true, List.of(ConfigKey.Scope.Account, ConfigKey.Scope.Domain, ConfigKey.Scope.Zone, ConfigKey.Scope.Global), null);
68+
true, List.of(ConfigKey.Scope.Account, ConfigKey.Scope.Domain, ConfigKey.Scope.Zone), null);
6969

7070
public static final ConfigKey<Boolean> VmStorageSnapshotKvm = new ConfigKey<>(Boolean.class, "kvm.vmstoragesnapshot.enabled", "Snapshots", "true", "For live snapshot of virtual machine instance on KVM hypervisor without memory. Requires qemu version 1.6+ (on NFS or Local file system) and qemu-guest-agent installed on guest VM", true, ConfigKey.Scope.Global, null);
7171

‎server/src/main/java/com/cloud/storage/snapshot/SnapshotSchedulerImpl.java‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -499,8 +499,16 @@ protected boolean shouldSkipUnchangedVolumeSnapshot(final VolumeVO volume) {
499499
return false;
500500
}
501501

502+
// The volume may have been written to on another instance and attached to this one after the last snapshot.
503+
// Volumes allocated together with their instance have no attach time and have been on it since creation.
504+
final Date attached = volume.getAttached();
505+
if (attached != null && !attached.before(lastSnapshot.getCreated())) {
506+
return false;
507+
}
508+
509+
// Only a confirmed powered off state proves the instance has not been running; PowerUnknown does not.
502510
final VMInstanceVO vm = vmInstanceDao.findById(instanceId);
503-
if (vm == null || vm.getPowerState() == VirtualMachine.PowerState.PowerOn) {
511+
if (vm == null || vm.getPowerState() != VirtualMachine.PowerState.PowerOff) {
504512
return false;
505513
}
506514

@@ -510,9 +518,13 @@ protected boolean shouldSkipUnchangedVolumeSnapshot(final VolumeVO volume) {
510518
return poweredOffSince != null && poweredOffSince.before(lastSnapshot.getCreated());
511519
}
512520

521+
/**
522+
* Returns the most recent successfully taken snapshot of the volume. Snapshots left in Allocated or Error
523+
* state by failed attempts are not a usable baseline and are ignored.
524+
*/
513525
protected SnapshotVO findLastSnapshot(final long volumeId) {
514526
final Filter filter = new Filter(SnapshotVO.class, "created", false, 0L, 1L);
515-
final List<SnapshotVO> snapshots = _snapshotDao.listByVolumeId(filter, volumeId);
527+
final List<SnapshotVO> snapshots = _snapshotDao.listByStatus(filter, volumeId, Snapshot.State.BackedUp);
516528
return (snapshots == null || snapshots.isEmpty()) ? null : snapshots.get(0);
517529
}
518530

‎server/src/test/java/com/cloud/storage/snapshot/SnapshotSchedulerImplTest.java‎

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,52 @@ public void shouldSkipUnchangedVolumeSnapshotTestVmOffSinceAfterLastSnapshotRetu
688688
Assert.assertFalse(result);
689689
}
690690

691+
@Test
692+
public void shouldSkipUnchangedVolumeSnapshotTestVmPowerUnknownReturnsFalse() {
693+
stubSkipConfigEnabled();
694+
doReturn(5L).when(volumeVoMock).getInstanceId();
695+
doReturn(1L).when(volumeVoMock).getId();
696+
doReturn(snapshotVoMock).when(snapshotSchedulerImplSpy).findLastSnapshot(Mockito.anyLong());
697+
doReturn(vmInstanceVoMock).when(vmInstanceDaoMock).findById(5L);
698+
doReturn(VirtualMachine.PowerState.PowerUnknown).when(vmInstanceVoMock).getPowerState();
699+
700+
boolean result = snapshotSchedulerImplSpy.shouldSkipUnchangedVolumeSnapshot(volumeVoMock);
701+
702+
Assert.assertFalse(result);
703+
}
704+
705+
@Test
706+
public void shouldSkipUnchangedVolumeSnapshotTestAttachedAfterLastSnapshotReturnsFalse() {
707+
stubSkipConfigEnabled();
708+
doReturn(5L).when(volumeVoMock).getInstanceId();
709+
doReturn(1L).when(volumeVoMock).getId();
710+
doReturn(new Date(3000L)).when(volumeVoMock).getAttached();
711+
doReturn(new Date(2000L)).when(snapshotVoMock).getCreated();
712+
doReturn(snapshotVoMock).when(snapshotSchedulerImplSpy).findLastSnapshot(Mockito.anyLong());
713+
714+
boolean result = snapshotSchedulerImplSpy.shouldSkipUnchangedVolumeSnapshot(volumeVoMock);
715+
716+
Assert.assertFalse(result);
717+
Mockito.verifyNoInteractions(vmInstanceDaoMock);
718+
}
719+
720+
@Test
721+
public void shouldSkipUnchangedVolumeSnapshotTestAttachedBeforeLastSnapshotAndVmOffReturnsTrue() {
722+
stubSkipConfigEnabled();
723+
doReturn(5L).when(volumeVoMock).getInstanceId();
724+
doReturn(1L).when(volumeVoMock).getId();
725+
doReturn(new Date(500L)).when(volumeVoMock).getAttached();
726+
doReturn(new Date(2000L)).when(snapshotVoMock).getCreated();
727+
doReturn(snapshotVoMock).when(snapshotSchedulerImplSpy).findLastSnapshot(Mockito.anyLong());
728+
doReturn(vmInstanceVoMock).when(vmInstanceDaoMock).findById(5L);
729+
doReturn(VirtualMachine.PowerState.PowerOff).when(vmInstanceVoMock).getPowerState();
730+
doReturn(new Date(1000L)).when(vmInstanceVoMock).getPowerStateUpdateTime();
731+
732+
boolean result = snapshotSchedulerImplSpy.shouldSkipUnchangedVolumeSnapshot(volumeVoMock);
733+
734+
Assert.assertTrue(result);
735+
}
736+
691737
private void stubSkipConfigEnabled() {
692738
doReturn(true).when(snapshotSchedulerImplSpy).getScopedConfigValue(Mockito.eq(SnapshotManager.SnapshotSkipIfVmNotRunning), Mockito.any(), Mockito.any());
693739
doReturn(1L).when(volumeVoMock).getAccountId();
@@ -734,7 +780,7 @@ public void skipAndRescheduleSnapshotTestCouldNotAcquireLockDoesNothing() {
734780

735781
@Test
736782
public void findLastSnapshotTestReturnsMostRecentSnapshot() {
737-
doReturn(List.of(snapshotVoMock)).when(snapshotDaoMock).listByVolumeId(Mockito.any(), Mockito.eq(1L));
783+
doReturn(List.of(snapshotVoMock)).when(snapshotDaoMock).listByStatus(Mockito.any(), Mockito.eq(1L), Mockito.eq(Snapshot.State.BackedUp));
738784

739785
SnapshotVO result = snapshotSchedulerImplSpy.findLastSnapshot(1L);
740786

@@ -743,7 +789,7 @@ public void findLastSnapshotTestReturnsMostRecentSnapshot() {
743789

744790
@Test
745791
public void findLastSnapshotTestNoSnapshotsReturnsNull() {
746-
doReturn(Collections.emptyList()).when(snapshotDaoMock).listByVolumeId(Mockito.any(), Mockito.eq(1L));
792+
doReturn(Collections.emptyList()).when(snapshotDaoMock).listByStatus(Mockito.any(), Mockito.eq(1L), Mockito.eq(Snapshot.State.BackedUp));
747793

748794
SnapshotVO result = snapshotSchedulerImplSpy.findLastSnapshot(1L);
749795

0 commit comments

Comments
 (0)