Skip to content

Block use of internal and external snapshots on KVM #11039

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: 4.20
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,6 @@ public interface SnapshotDao extends GenericDao<SnapshotVO, Long>, StateDao<Snap
*/
List<SnapshotVO> listByIds(Object... ids);
List<SnapshotVO> searchByVolumes(List<Long> volumeIds);

List<SnapshotVO> listByVolumeIdAndTypeNotInAndStateNotRemoved(long volumeId, Type... types);
}
108 changes: 69 additions & 39 deletions engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDaoImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.annotation.PostConstruct;
Expand Down Expand Up @@ -56,6 +57,17 @@ public class SnapshotDaoImpl extends GenericDaoBase<SnapshotVO, Long> implements
private static final String GET_LAST_SNAPSHOT =
"SELECT snapshots.id FROM snapshot_store_ref, snapshots where snapshots.id = snapshot_store_ref.snapshot_id AND snapshosts.volume_id = ? AND snapshot_store_ref.role = ? ORDER BY created DESC";

private static final String VOLUME_ID = "volumeId";
private static final String REMOVED = "removed";
private static final String ID = "id";
private static final String INSTANCE_ID = "instanceId";
private static final String STATE = "state";
private static final String INSTANCE_VOLUMES = "instanceVolumes";
private static final String INSTANCE_SNAPSHOTS = "instanceSnapshots";
private static final String VERSION = "version";
private static final String ACCOUNT_ID = "accountId";
private static final String NOT_TYPE = "notType";

private SearchBuilder<SnapshotVO> snapshotIdsSearch;
private SearchBuilder<SnapshotVO> VolumeIdSearch;
private SearchBuilder<SnapshotVO> VolumeIdTypeSearch;
Expand All @@ -66,6 +78,8 @@ public class SnapshotDaoImpl extends GenericDaoBase<SnapshotVO, Long> implements
private SearchBuilder<SnapshotVO> StatusSearch;
private SearchBuilder<SnapshotVO> notInStatusSearch;
private GenericSearchBuilder<SnapshotVO, Long> CountSnapshotsByAccount;

private SearchBuilder<SnapshotVO> volumeIdAndTypeNotInSearch;
@Inject
ResourceTagDao _tagsDao;
@Inject
Expand All @@ -76,9 +90,9 @@ public class SnapshotDaoImpl extends GenericDaoBase<SnapshotVO, Long> implements
@Override
public List<SnapshotVO> listByVolumeIdTypeNotDestroyed(long volumeId, Type type) {
SearchCriteria<SnapshotVO> sc = VolumeIdTypeNotDestroyedSearch.create();
sc.setParameters("volumeId", volumeId);
sc.setParameters(VOLUME_ID, volumeId);
sc.setParameters("type", type.ordinal());
sc.setParameters("status", State.Destroyed);
sc.setParameters(STATE, State.Destroyed);
return listBy(sc, null);
}

Expand All @@ -95,28 +109,28 @@ public List<SnapshotVO> listByVolumeId(long volumeId) {
@Override
public List<SnapshotVO> listByVolumeId(Filter filter, long volumeId) {
SearchCriteria<SnapshotVO> sc = VolumeIdSearch.create();
sc.setParameters("volumeId", volumeId);
sc.setParameters(VOLUME_ID, volumeId);
return listBy(sc, filter);
}

@Override
public List<SnapshotVO> listByVolumeIdIncludingRemoved(long volumeId) {
SearchCriteria<SnapshotVO> sc = VolumeIdSearch.create();
sc.setParameters("volumeId", volumeId);
sc.setParameters(VOLUME_ID, volumeId);
return listIncludingRemovedBy(sc, null);
}

public List<SnapshotVO> listByVolumeIdType(Filter filter, long volumeId, Type type) {
SearchCriteria<SnapshotVO> sc = VolumeIdTypeSearch.create();
sc.setParameters("volumeId", volumeId);
sc.setParameters(VOLUME_ID, volumeId);
sc.setParameters("type", type.ordinal());
return listBy(sc, filter);
}

public List<SnapshotVO> listByVolumeIdVersion(Filter filter, long volumeId, String version) {
SearchCriteria<SnapshotVO> sc = VolumeIdVersionSearch.create();
sc.setParameters("volumeId", volumeId);
sc.setParameters("version", version);
sc.setParameters(VOLUME_ID, volumeId);
sc.setParameters(VERSION, version);
return listBy(sc, filter);
}

Expand All @@ -126,61 +140,67 @@ public SnapshotDaoImpl() {
@PostConstruct
protected void init() {
VolumeIdSearch = createSearchBuilder();
VolumeIdSearch.and("volumeId", VolumeIdSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
VolumeIdSearch.and(VOLUME_ID, VolumeIdSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
VolumeIdSearch.done();

VolumeIdTypeSearch = createSearchBuilder();
VolumeIdTypeSearch.and("volumeId", VolumeIdTypeSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
VolumeIdTypeSearch.and(VOLUME_ID, VolumeIdTypeSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
VolumeIdTypeSearch.and("type", VolumeIdTypeSearch.entity().getSnapshotType(), SearchCriteria.Op.EQ);
VolumeIdTypeSearch.done();

VolumeIdTypeNotDestroyedSearch = createSearchBuilder();
VolumeIdTypeNotDestroyedSearch.and("volumeId", VolumeIdTypeNotDestroyedSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
VolumeIdTypeNotDestroyedSearch.and(VOLUME_ID, VolumeIdTypeNotDestroyedSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
VolumeIdTypeNotDestroyedSearch.and("type", VolumeIdTypeNotDestroyedSearch.entity().getSnapshotType(), SearchCriteria.Op.EQ);
VolumeIdTypeNotDestroyedSearch.and("status", VolumeIdTypeNotDestroyedSearch.entity().getState(), SearchCriteria.Op.NEQ);
VolumeIdTypeNotDestroyedSearch.and(STATE, VolumeIdTypeNotDestroyedSearch.entity().getState(), SearchCriteria.Op.NEQ);
VolumeIdTypeNotDestroyedSearch.done();

VolumeIdVersionSearch = createSearchBuilder();
VolumeIdVersionSearch.and("volumeId", VolumeIdVersionSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
VolumeIdVersionSearch.and("version", VolumeIdVersionSearch.entity().getVersion(), SearchCriteria.Op.EQ);
VolumeIdVersionSearch.and(VOLUME_ID, VolumeIdVersionSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
VolumeIdVersionSearch.and(VERSION, VolumeIdVersionSearch.entity().getVersion(), SearchCriteria.Op.EQ);
VolumeIdVersionSearch.done();

AccountIdSearch = createSearchBuilder();
AccountIdSearch.and("accountId", AccountIdSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
AccountIdSearch.and(ACCOUNT_ID, AccountIdSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
AccountIdSearch.done();

StatusSearch = createSearchBuilder();
StatusSearch.and("volumeId", StatusSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
StatusSearch.and("status", StatusSearch.entity().getState(), SearchCriteria.Op.IN);
StatusSearch.and(VOLUME_ID, StatusSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
StatusSearch.and(STATE, StatusSearch.entity().getState(), SearchCriteria.Op.IN);
StatusSearch.done();

notInStatusSearch = createSearchBuilder();
notInStatusSearch.and("volumeId", notInStatusSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
notInStatusSearch.and("status", notInStatusSearch.entity().getState(), SearchCriteria.Op.NOTIN);
notInStatusSearch.and(VOLUME_ID, notInStatusSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
notInStatusSearch.and(STATE, notInStatusSearch.entity().getState(), SearchCriteria.Op.NOTIN);
notInStatusSearch.done();

CountSnapshotsByAccount = createSearchBuilder(Long.class);
CountSnapshotsByAccount.select(null, Func.COUNT, null);
CountSnapshotsByAccount.and("account", CountSnapshotsByAccount.entity().getAccountId(), SearchCriteria.Op.EQ);
CountSnapshotsByAccount.and("status", CountSnapshotsByAccount.entity().getState(), SearchCriteria.Op.NIN);
CountSnapshotsByAccount.and("removed", CountSnapshotsByAccount.entity().getRemoved(), SearchCriteria.Op.NULL);
CountSnapshotsByAccount.and(ACCOUNT_ID, CountSnapshotsByAccount.entity().getAccountId(), SearchCriteria.Op.EQ);
CountSnapshotsByAccount.and(STATE, CountSnapshotsByAccount.entity().getState(), SearchCriteria.Op.NIN);
CountSnapshotsByAccount.and(REMOVED, CountSnapshotsByAccount.entity().getRemoved(), SearchCriteria.Op.NULL);
CountSnapshotsByAccount.done();

InstanceIdSearch = createSearchBuilder();
InstanceIdSearch.and("status", InstanceIdSearch.entity().getState(), SearchCriteria.Op.IN);
InstanceIdSearch.and(STATE, InstanceIdSearch.entity().getState(), SearchCriteria.Op.IN);

snapshotIdsSearch = createSearchBuilder();
snapshotIdsSearch.and("id", snapshotIdsSearch.entity().getId(), SearchCriteria.Op.IN);
snapshotIdsSearch.and(ID, snapshotIdsSearch.entity().getId(), SearchCriteria.Op.IN);

SearchBuilder<VMInstanceVO> instanceSearch = _instanceDao.createSearchBuilder();
instanceSearch.and("instanceId", instanceSearch.entity().getId(), SearchCriteria.Op.EQ);
instanceSearch.and(INSTANCE_ID, instanceSearch.entity().getId(), SearchCriteria.Op.EQ);

SearchBuilder<VolumeVO> volumeSearch = _volumeDao.createSearchBuilder();
volumeSearch.and("state", volumeSearch.entity().getState(), SearchCriteria.Op.EQ);
volumeSearch.join("instanceVolumes", instanceSearch, instanceSearch.entity().getId(), volumeSearch.entity().getInstanceId(), JoinType.INNER);
volumeSearch.and(STATE, volumeSearch.entity().getState(), SearchCriteria.Op.EQ);
volumeSearch.join(INSTANCE_VOLUMES, instanceSearch, instanceSearch.entity().getId(), volumeSearch.entity().getInstanceId(), JoinType.INNER);

InstanceIdSearch.join("instanceSnapshots", volumeSearch, volumeSearch.entity().getId(), InstanceIdSearch.entity().getVolumeId(), JoinType.INNER);
InstanceIdSearch.join(INSTANCE_SNAPSHOTS, volumeSearch, volumeSearch.entity().getId(), InstanceIdSearch.entity().getVolumeId(), JoinType.INNER);
InstanceIdSearch.done();

volumeIdAndTypeNotInSearch = createSearchBuilder();
volumeIdAndTypeNotInSearch.and(VOLUME_ID, volumeIdAndTypeNotInSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
volumeIdAndTypeNotInSearch.and(STATE, volumeIdAndTypeNotInSearch.entity().getState(), SearchCriteria.Op.NEQ);
volumeIdAndTypeNotInSearch.and(NOT_TYPE, volumeIdAndTypeNotInSearch.entity().getTypeDescription(), SearchCriteria.Op.NOTIN);
volumeIdAndTypeNotInSearch.done();
}

@Override
Expand All @@ -205,8 +225,8 @@ public long getLastSnapshot(long volumeId, DataStoreRole role) {
@Override
public Long countSnapshotsForAccount(long accountId) {
SearchCriteria<Long> sc = CountSnapshotsByAccount.create();
sc.setParameters("account", accountId);
sc.setParameters("status", State.Error, State.Destroyed);
sc.setParameters(ACCOUNT_ID, accountId);
sc.setParameters(STATE, State.Error, State.Destroyed);
return customSearch(sc, null).get(0);
}

Expand All @@ -215,19 +235,19 @@ public List<SnapshotVO> listByInstanceId(long instanceId, Snapshot.State... stat
SearchCriteria<SnapshotVO> sc = InstanceIdSearch.create();

if (status != null && status.length != 0) {
sc.setParameters("status", (Object[])status);
sc.setParameters(STATE, (Object[])status);
}

sc.setJoinParameters("instanceSnapshots", "state", Volume.State.Ready);
sc.setJoinParameters("instanceVolumes", "instanceId", instanceId);
sc.setJoinParameters(INSTANCE_SNAPSHOTS, STATE, Volume.State.Ready);
sc.setJoinParameters(INSTANCE_VOLUMES, INSTANCE_ID, instanceId);
return listBy(sc, null);
}

@Override
public List<SnapshotVO> listByStatus(long volumeId, Snapshot.State... status) {
SearchCriteria<SnapshotVO> sc = StatusSearch.create();
sc.setParameters("volumeId", volumeId);
sc.setParameters("status", (Object[])status);
sc.setParameters(VOLUME_ID, volumeId);
sc.setParameters(STATE, (Object[])status);
return listBy(sc, null);
}

Expand All @@ -248,14 +268,14 @@ public boolean remove(Long id) {
@Override
public List<SnapshotVO> listAllByStatus(Snapshot.State... status) {
SearchCriteria<SnapshotVO> sc = StatusSearch.create();
sc.setParameters("status", (Object[])status);
sc.setParameters(STATE, (Object[])status);
return listBy(sc, null);
}

@Override
public List<SnapshotVO> listByIds(Object... ids) {
SearchCriteria<SnapshotVO> sc = snapshotIdsSearch.create();
sc.setParameters("id", ids);
sc.setParameters(ID, ids);
return listBy(sc, null);
}

Expand All @@ -273,7 +293,7 @@ public boolean updateState(State currentState, Event event, State nextState, Sna
@Override
public void updateVolumeIds(long oldVolId, long newVolId) {
SearchCriteria<SnapshotVO> sc = VolumeIdSearch.create();
sc.setParameters("volumeId", oldVolId);
sc.setParameters(VOLUME_ID, oldVolId);
SnapshotVO snapshot = createForUpdate();
snapshot.setVolumeId(newVolId);
UpdateBuilder ub = getUpdateBuilder(snapshot);
Expand All @@ -283,8 +303,8 @@ public void updateVolumeIds(long oldVolId, long newVolId) {
@Override
public List<SnapshotVO> listByStatusNotIn(long volumeId, Snapshot.State... status) {
SearchCriteria<SnapshotVO> sc = this.notInStatusSearch.create();
sc.setParameters("volumeId", volumeId);
sc.setParameters("status", (Object[]) status);
sc.setParameters(VOLUME_ID, volumeId);
sc.setParameters(STATE, (Object[]) status);
return listBy(sc, null);
}

Expand All @@ -299,4 +319,14 @@ public List<SnapshotVO> searchByVolumes(List<Long> volumeIds) {
sc.setParameters("volumeIds", volumeIds.toArray());
return search(sc, null);
}

@Override
public List<SnapshotVO> listByVolumeIdAndTypeNotInAndStateNotRemoved(long volumeId, Type... types) {
SearchCriteria<SnapshotVO> sc = volumeIdAndTypeNotInSearch.create();
sc.setParameters(VOLUME_ID, volumeId);
sc.setParameters(NOT_TYPE, Arrays.stream(types).map(Type::toString).toArray());
sc.setParameters(STATE, State.Destroyed);

return listBy(sc);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public interface VMSnapshotDao extends GenericDao<VMSnapshotVO, Long>, StateDao<

List<VMSnapshotVO> findByVm(Long vmId);

List<VMSnapshotVO> findByVmAndByType(Long vmId, VMSnapshot.Type type);

List<VMSnapshotVO> listExpungingSnapshot();

List<VMSnapshotVO> listByInstanceId(Long vmId, VMSnapshot.State... status);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ public List<VMSnapshotVO> findByVm(Long vmId) {
return listBy(sc, null);
}

@Override
public List<VMSnapshotVO> findByVmAndByType(Long vmId, VMSnapshot.Type type) {
SearchCriteria<VMSnapshotVO> sc = AllFieldsSearch.create();
sc.setParameters("vm_id", vmId);
sc.setParameters("vm_snapshot_type", type);
return listBy(sc, null);
}

@Override
public List<VMSnapshotVO> listExpungingSnapshot() {
SearchCriteria<VMSnapshotVO> sc = ExpungingSnapshotSearch.create();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

import javax.inject.Inject;

import com.cloud.vm.snapshot.VMSnapshot;
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event;
Expand Down Expand Up @@ -100,6 +102,9 @@ public class DefaultSnapshotStrategy extends SnapshotStrategyBase {
@Inject
SnapshotZoneDao snapshotZoneDao;

@Inject
private VMSnapshotDao vmSnapshotDao;

private final List<Snapshot.State> snapshotStatesAbleToDeleteSnapshot = Arrays.asList(Snapshot.State.Destroying, Snapshot.State.Destroyed, Snapshot.State.Error);

public SnapshotDataStoreVO getSnapshotImageStoreRef(long snapshotId, long zoneId) {
Expand Down Expand Up @@ -571,6 +576,9 @@ public void doInTransactionWithoutResult(TransactionStatus status) {

@Override
public StrategyPriority canHandle(Snapshot snapshot, Long zoneId, SnapshotOperation op) {
if (SnapshotOperation.TAKE.equals(op)) {
return canHandleTake(snapshot);
}
if (SnapshotOperation.REVERT.equals(op)) {
long volumeId = snapshot.getVolumeId();
VolumeVO volumeVO = volumeDao.findById(volumeId);
Expand All @@ -597,4 +605,16 @@ protected boolean isSnapshotStoredOnSameZoneStoreForQCOW2Volume(Snapshot snapsho
dataStoreMgr.getStoreZoneId(s.getDataStoreId(), s.getRole()), volumeVO.getDataCenterId()));
}

private StrategyPriority canHandleTake(Snapshot snapshot) {
VolumeVO volumeVO = volumeDao.findById(snapshot.getVolumeId());
if (volumeVO.getInstanceId() == null) {
return StrategyPriority.DEFAULT;
}
if (CollectionUtils.isNotEmpty(vmSnapshotDao.findByVmAndByType(volumeVO.getInstanceId(), VMSnapshot.Type.DiskAndMemory))) {
logger.debug("DefaultSnapshotStrategy cannot handle snapshot [{}] for volume [{}] as the volume is attached to a VM with disk-and-memory VM snapshots." +
"Restoring the volume snapshot will corrupt any newer disk-and-memory VM snapshots.", snapshot);
return StrategyPriority.CANT_HANDLE;
}
return StrategyPriority.DEFAULT;
}
}
Loading
Loading