Add and improve logs in snapshot scheduling (#6925)

Co-authored-by: Stephan Krug <stephan.krug@scclouds.com.br>
This commit is contained in:
Stephan Krug 2023-09-19 03:54:52 -03:00 committed by GitHub
parent d25521e96f
commit c428d3bb34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 282 additions and 26 deletions

View File

@ -264,10 +264,10 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu
@DB
protected void scheduleSnapshots() {
String displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, _currentTimestamp);
s_logger.debug("Snapshot scheduler.poll is being called at " + displayTime);
s_logger.debug(String.format("Snapshot scheduler is being called at [%s].", displayTime));
final List<SnapshotScheduleVO> snapshotsToBeExecuted = _snapshotScheduleDao.getSchedulesToExecute(_currentTimestamp);
s_logger.debug("Got " + snapshotsToBeExecuted.size() + " snapshots to be executed at " + displayTime);
s_logger.debug(String.format("There are [%s] scheduled snapshots to be executed at [%s].", snapshotsToBeExecuted.size(), displayTime));
for (final SnapshotScheduleVO snapshotToBeExecuted : snapshotsToBeExecuted) {
SnapshotScheduleVO tmpSnapshotScheduleVO = null;
@ -275,34 +275,18 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu
final long policyId = snapshotToBeExecuted.getPolicyId();
final long volumeId = snapshotToBeExecuted.getVolumeId();
try {
final VolumeVO volume = _volsDao.findById(volumeId);
if (volume.getPoolId() == null) {
// this volume is not attached
final VolumeVO volume = _volsDao.findByIdIncludingRemoved(snapshotToBeExecuted.getVolumeId());
if (!canSnapshotBeScheduled(snapshotToBeExecuted, volume)) {
continue;
}
Account volAcct = _acctDao.findById(volume.getAccountId());
if (volAcct == null || volAcct.getState() == Account.State.DISABLED) {
// this account has been removed, so don't trigger recurring snapshot
if (s_logger.isDebugEnabled()) {
s_logger.debug("Skip snapshot for volume " + volume.getUuid() + " since its account has been removed or disabled");
}
continue;
}
if (_snapshotPolicyDao.findById(policyId) == null) {
_snapshotScheduleDao.remove(snapshotToBeExecuted.getId());
}
if (s_logger.isDebugEnabled()) {
final Date scheduledTimestamp = snapshotToBeExecuted.getScheduledTimestamp();
displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, scheduledTimestamp);
s_logger.debug("Scheduling 1 snapshot for volume id " + volumeId + " (volume name:" +
volume.getName() + ") for schedule id: " + snapshotToBeExecuted.getId() + " at " + displayTime);
}
tmpSnapshotScheduleVO = _snapshotScheduleDao.acquireInLockTable(snapshotScheId);
final Long eventId =
ActionEventUtils.onScheduledActionEvent(User.UID_SYSTEM, volume.getAccountId(), EventTypes.EVENT_SNAPSHOT_CREATE, "creating snapshot for volume Id:" +
volume.getUuid(), volumeId, ApiCommandResourceType.Volume.toString(), true, 0);
s_logger.trace(String.format("Mapping parameters required to generate a CreateSnapshotCmd for snapshot [%s].", snapshotToBeExecuted.getUuid()));
final Map<String, String> params = new HashMap<String, String>();
params.put(ApiConstants.VOLUME_ID, "" + volumeId);
params.put(ApiConstants.POLICY_ID, "" + policyId);
@ -319,24 +303,27 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu
}
}
s_logger.trace(String.format("Generating a CreateSnapshotCmd for snapshot [%s] with parameters: [%s].", snapshotToBeExecuted.getUuid(), params.toString()));
final CreateSnapshotCmd cmd = new CreateSnapshotCmd();
ComponentContext.inject(cmd);
_dispatcher.dispatchCreateCmd(cmd, params);
params.put("id", "" + cmd.getEntityId());
params.put("ctxStartEventId", "1");
final Date scheduledTimestamp = snapshotToBeExecuted.getScheduledTimestamp();
displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, scheduledTimestamp);
s_logger.debug(String.format("Scheduling snapshot [%s] for volume [%s] at [%s].", snapshotToBeExecuted.getUuid(), volume.getVolumeDescription(), displayTime));
AsyncJobVO job = new AsyncJobVO("", User.UID_SYSTEM, volume.getAccountId(), CreateSnapshotCmd.class.getName(),
ApiGsonHelper.getBuilder().create().toJson(params), cmd.getEntityId(),
cmd.getApiResourceType() != null ? cmd.getApiResourceType().toString() : null, null);
job.setDispatcher(_asyncDispatcher.getName());
final long jobId = _asyncMgr.submitAsyncJob(job);
s_logger.debug(String.format("Scheduled snapshot [%s] for volume [%s] as job [%s].", snapshotToBeExecuted.getUuid(), volume.getVolumeDescription(), job.getUuid()));
tmpSnapshotScheduleVO.setAsyncJobId(jobId);
_snapshotScheduleDao.update(snapshotScheId, tmpSnapshotScheduleVO);
} catch (final Exception e) {
// TODO Logging this exception is enough?
s_logger.warn("Scheduling snapshot failed due to " + e.toString());
s_logger.error(String.format("The scheduling of snapshot [%s] for volume [%s] failed due to [%s].", snapshotToBeExecuted.getUuid(), volumeId, e.toString()), e);
} finally {
if (tmpSnapshotScheduleVO != null) {
_snapshotScheduleDao.releaseFromLockTable(snapshotScheId);
@ -345,7 +332,59 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu
}
}
private Date scheduleNextSnapshotJob(final SnapshotScheduleVO snapshotSchedule) {
/**
* Verifies if a snapshot for a volume can be scheduled or not based on volume and account status, and removes it from the snapshot scheduler if its policy was removed.
*
* @param snapshotToBeScheduled the snapshot to be scheduled
* @param volume the volume associated with the snapshot to be scheduled
* @return <code>true</code> if the snapshot can be scheduled, and <code>false</code> otherwise.
*/
protected boolean canSnapshotBeScheduled(final SnapshotScheduleVO snapshotToBeScheduled, final VolumeVO volume) {
if (volume.getRemoved() != null) {
s_logger.warn(String.format("Skipping snapshot [%s] for volume [%s] because it has been removed. Having a snapshot scheduled for a volume that has been "
+ "removed is an inconsistency; please, check your database.", snapshotToBeScheduled.getUuid(), volume.getVolumeDescription()));
return false;
}
if (volume.getPoolId() == null) {
s_logger.debug(String.format("Skipping snapshot [%s] for volume [%s] because it is not attached to any storage pool.", snapshotToBeScheduled.getUuid(),
volume.getVolumeDescription()));
return false;
}
if (isAccountRemovedOrDisabled(snapshotToBeScheduled, volume)) {
return false;
}
if (_snapshotPolicyDao.findById(snapshotToBeScheduled.getPolicyId()) == null) {
s_logger.debug(String.format("Snapshot's policy [%s] for volume [%s] has been removed; therefore, this snapshot will be removed from the snapshot scheduler.",
snapshotToBeScheduled.getPolicyId(), volume.getVolumeDescription()));
_snapshotScheduleDao.remove(snapshotToBeScheduled.getId());
}
s_logger.debug(String.format("Snapshot [%s] for volume [%s] can be executed.", snapshotToBeScheduled.getUuid(), volume.getVolumeDescription()));
return true;
}
protected boolean isAccountRemovedOrDisabled(final SnapshotScheduleVO snapshotToBeExecuted, final VolumeVO volume) {
Account volAcct = _acctDao.findById(volume.getAccountId());
if (volAcct == null) {
s_logger.debug(String.format("Skipping snapshot [%s] for volume [%s] because its account [%s] has been removed.", snapshotToBeExecuted.getUuid(),
volume.getVolumeDescription(), volume.getAccountId()));
return true;
}
if (volAcct.getState() == Account.State.DISABLED) {
s_logger.debug(String.format("Skipping snapshot [%s] for volume [%s] because its account [%s] is disabled.", snapshotToBeExecuted.getUuid(),
volume.getVolumeDescription(), volAcct.getUuid()));
return true;
}
return false;
}
protected Date scheduleNextSnapshotJob(final SnapshotScheduleVO snapshotSchedule) {
if (snapshotSchedule == null) {
return null;
}

View File

@ -0,0 +1,217 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.storage.snapshot;
import com.cloud.storage.Snapshot;
import com.cloud.storage.SnapshotPolicyVO;
import com.cloud.storage.SnapshotScheduleVO;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.SnapshotPolicyDao;
import com.cloud.storage.dao.SnapshotScheduleDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.user.Account;
import com.cloud.user.AccountVO;
import com.cloud.user.dao.AccountDao;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Date;
@RunWith(MockitoJUnitRunner.class)
public class SnapshotSchedulerImplTest {
@Spy
@InjectMocks
SnapshotSchedulerImpl snapshotSchedulerImplSpy = new SnapshotSchedulerImpl();
@Mock
SnapshotPolicyDao snapshotPolicyDaoMock;
@Mock
SnapshotPolicyVO snapshotPolicyVoMock;
@Mock
SnapshotScheduleDao snapshotScheduleDaoMock;
@Mock
AccountDao accountDaoMock;
@Mock
VolumeDao volumeDaoMock;
@Mock
VolumeVO volumeVoMock;
@Mock
AccountVO accountVoMock;
@Test
public void scheduleNextSnapshotJobTestParameterIsNullReturnNull() {
SnapshotScheduleVO snapshotScheduleVO = null;
Date result = snapshotSchedulerImplSpy.scheduleNextSnapshotJob(snapshotScheduleVO);
Assert.assertNull(result);
}
@Test
public void scheduleNextSnapshotJobTestIsManualPolicyIdReturnNull() {
SnapshotScheduleVO snapshotScheduleVO = new SnapshotScheduleVO();
snapshotScheduleVO.setPolicyId(Snapshot.MANUAL_POLICY_ID);
Date result = snapshotSchedulerImplSpy.scheduleNextSnapshotJob(snapshotScheduleVO);
Assert.assertNull(result);
}
@Test
public void scheduleNextSnapshotJobTestPolicyIsNotNullDoNotCallExpunge() {
Date expected = new Date();
SnapshotScheduleVO snapshotScheduleVO = new SnapshotScheduleVO();
snapshotScheduleVO.setPolicyId(1l);
Mockito.doReturn(snapshotPolicyVoMock).when(snapshotPolicyDaoMock).findById(Mockito.anyLong());
Mockito.doReturn(expected).when(snapshotSchedulerImplSpy).scheduleNextSnapshotJob(Mockito.any(SnapshotPolicyVO.class));
Date result = snapshotSchedulerImplSpy.scheduleNextSnapshotJob(snapshotScheduleVO);
Assert.assertEquals(expected, result);
Mockito.verify(snapshotScheduleDaoMock, Mockito.never()).expunge(Mockito.anyLong());
}
@Test
public void scheduleNextSnapshotJobTestPolicyIsNullCallExpunge() {
Date expected = new Date();
SnapshotPolicyVO snapshotPolicyVO = null;
SnapshotScheduleVO snapshotScheduleVO = new SnapshotScheduleVO();
snapshotScheduleVO.setPolicyId(1l);
Mockito.doReturn(snapshotPolicyVO).when(snapshotPolicyDaoMock).findById(Mockito.anyLong());
Mockito.doReturn(true).when(snapshotScheduleDaoMock).expunge(Mockito.anyLong());
Mockito.doReturn(expected).when(snapshotSchedulerImplSpy).scheduleNextSnapshotJob(snapshotPolicyVO);
Date result = snapshotSchedulerImplSpy.scheduleNextSnapshotJob(snapshotScheduleVO);
Assert.assertEquals(expected, result);
Mockito.verify(snapshotScheduleDaoMock).expunge(Mockito.anyLong());
}
@Test
public void isAccountRemovedOrDisabledTestVolumeAccountIsNullReturnTrue() {
SnapshotScheduleVO snapshotScheduleVO = new SnapshotScheduleVO();
Mockito.doReturn(null).when(accountDaoMock).findById(Mockito.anyLong());
boolean result = snapshotSchedulerImplSpy.isAccountRemovedOrDisabled(snapshotScheduleVO, volumeVoMock);
Assert.assertTrue(result);
}
@Test
public void isAccountRemovedOrDisabledTestVolumeAccountStateIsDisabledReturnTrue() {
SnapshotScheduleVO snapshotScheduleVO = new SnapshotScheduleVO();
Mockito.doReturn(accountVoMock).when(accountDaoMock).findById(Mockito.anyLong());
Mockito.doReturn(Account.State.DISABLED).when(accountVoMock).getState();
boolean result = snapshotSchedulerImplSpy.isAccountRemovedOrDisabled(snapshotScheduleVO, volumeVoMock);
Assert.assertTrue(result);
}
@Test
public void isAccountRemovedOrDisabledTestVolumeAccountStateIsNotNullNorDisabledReturnFalse() {
SnapshotScheduleVO snapshotScheduleVO = new SnapshotScheduleVO();
Mockito.doReturn(accountVoMock).when(accountDaoMock).findById(Mockito.anyLong());
Mockito.doReturn(Account.State.ENABLED).when(accountVoMock).getState();
boolean result = snapshotSchedulerImplSpy.isAccountRemovedOrDisabled(snapshotScheduleVO, volumeVoMock);
Assert.assertFalse(result);
}
@Test
public void canSnapshotBeScheduledTestVolumeIsRemovedReturnFalse() {
SnapshotScheduleVO snapshotScheduleVO = new SnapshotScheduleVO();
Mockito.doReturn(new Date()).when(volumeVoMock).getRemoved();
boolean result = snapshotSchedulerImplSpy.canSnapshotBeScheduled(snapshotScheduleVO, volumeVoMock);
Assert.assertFalse(result);
}
@Test
public void canSnapshotBeScheduledTestVolumeIsNotAttachedToStoragePoolReturnFalse() {
SnapshotScheduleVO snapshotScheduleVO = new SnapshotScheduleVO();
Mockito.doReturn(null).when(volumeVoMock).getPoolId();
boolean result = snapshotSchedulerImplSpy.canSnapshotBeScheduled(snapshotScheduleVO, volumeVoMock);
Assert.assertFalse(result);
}
@Test
public void canSnapshotBeScheduledTestAccountIsRemovedOrDisabledReturnFalse() {
SnapshotScheduleVO snapshotScheduleVO = new SnapshotScheduleVO();
Mockito.doReturn(1l).when(volumeVoMock).getPoolId();
Mockito.doReturn(true).when(snapshotSchedulerImplSpy).isAccountRemovedOrDisabled(Mockito.any(), Mockito.any());
boolean result = snapshotSchedulerImplSpy.canSnapshotBeScheduled(snapshotScheduleVO, volumeVoMock);
Assert.assertFalse(result);
}
@Test
public void canSnapshotBeScheduledTestSnapshotPolicyIsRemovedCallRemove() {
SnapshotScheduleVO snapshotScheduleVO = new SnapshotScheduleVO();
Mockito.doReturn(1l).when(volumeVoMock).getPoolId();
Mockito.doReturn(false).when(snapshotSchedulerImplSpy).isAccountRemovedOrDisabled(Mockito.any(), Mockito.any());
Mockito.doReturn(null).when(snapshotPolicyDaoMock).findById(Mockito.any());
boolean result = snapshotSchedulerImplSpy.canSnapshotBeScheduled(snapshotScheduleVO, volumeVoMock);
Assert.assertTrue(result);
Mockito.verify(snapshotScheduleDaoMock).remove(Mockito.anyLong());
}
@Test
public void canSnapshotBeScheduledTestSnapshotPolicyIsNotRemovedDoNotCallRemove() {
SnapshotScheduleVO snapshotScheduleVO = new SnapshotScheduleVO();
SnapshotPolicyVO snapshotPolicyVO = new SnapshotPolicyVO();
Mockito.doReturn(1l).when(volumeVoMock).getPoolId();
Mockito.doReturn(false).when(snapshotSchedulerImplSpy).isAccountRemovedOrDisabled(Mockito.any(), Mockito.any());
Mockito.doReturn(snapshotPolicyVO).when(snapshotPolicyDaoMock).findById(Mockito.any());
boolean result = snapshotSchedulerImplSpy.canSnapshotBeScheduled(snapshotScheduleVO, volumeVoMock);
Assert.assertTrue(result);
Mockito.verify(snapshotScheduleDaoMock, Mockito.never()).remove(Mockito.anyLong());
}
}