mirror of
https://github.com/apache/cloudstack.git
synced 2025-11-02 11:52:28 +01:00
Adding a snapshot strategy for systems that want to take snapshots that reside on their own system (as opposed to the default approach of taking a hypervisor snapshot and then copying it to secondary storage)
This commit is contained in:
parent
4b8bfe2627
commit
35a8b70799
@ -19,5 +19,6 @@
|
||||
package org.apache.cloudstack.engine.subsystem.api.storage;
|
||||
|
||||
public enum DataStoreCapabilities {
|
||||
VOLUME_SNAPSHOT_QUIESCEVM
|
||||
VOLUME_SNAPSHOT_QUIESCEVM,
|
||||
STORAGE_SYSTEM_SNAPSHOT // indicates to the StorageSystemSnapshotStrategy that this driver takes snapshots on its own system
|
||||
}
|
||||
|
||||
@ -30,6 +30,9 @@
|
||||
<bean id="xenserverSnapshotStrategy"
|
||||
class="org.apache.cloudstack.storage.snapshot.XenserverSnapshotStrategy" />
|
||||
|
||||
<bean id="storageSystemSnapshotStrategy"
|
||||
class="org.apache.cloudstack.storage.snapshot.StorageSystemSnapshotStrategy" />
|
||||
|
||||
<bean id="DefaultVMSnapshotStrategy"
|
||||
class="org.apache.cloudstack.storage.vmsnapshot.DefaultVMSnapshotStrategy" />
|
||||
|
||||
|
||||
@ -0,0 +1,208 @@
|
||||
// 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 org.apache.cloudstack.storage.snapshot;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotResult;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.Snapshot;
|
||||
import com.cloud.storage.SnapshotVO;
|
||||
import com.cloud.storage.Volume;
|
||||
import com.cloud.storage.VolumeVO;
|
||||
import com.cloud.storage.dao.SnapshotDao;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.utils.db.DB;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.fsm.NoTransitionException;
|
||||
|
||||
@Component
|
||||
public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
|
||||
private static final Logger s_logger = Logger.getLogger(StorageSystemSnapshotStrategy.class);
|
||||
|
||||
@Inject private DataStoreManager _dataStoreMgr;
|
||||
@Inject private SnapshotDao _snapshotDao;
|
||||
@Inject private SnapshotDataFactory _snapshotDataFactory;
|
||||
@Inject private SnapshotDataStoreDao _snapshotStoreDao;
|
||||
@Inject private PrimaryDataStoreDao _storagePoolDao;
|
||||
@Inject private VolumeDao _volumeDao;
|
||||
|
||||
@Override
|
||||
public SnapshotInfo backupSnapshot(SnapshotInfo snapshotInfo) {
|
||||
return snapshotInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteSnapshot(Long snapshotId) {
|
||||
SnapshotVO snapshotVO = _snapshotDao.findById(snapshotId);
|
||||
|
||||
if (Snapshot.State.Destroyed.equals(snapshotVO.getState())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Snapshot.State.Error.equals(snapshotVO.getState())) {
|
||||
_snapshotDao.remove(snapshotId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Snapshot.State.BackedUp.equals(snapshotVO.getState())) {
|
||||
throw new InvalidParameterValueException("Unable to delete snapshotshot " + snapshotId + " because it is in the following state: " + snapshotVO.getState());
|
||||
}
|
||||
|
||||
SnapshotObject snapshotObj = (SnapshotObject)_snapshotDataFactory.getSnapshot(snapshotId, DataStoreRole.Primary);
|
||||
|
||||
if (snapshotObj == null) {
|
||||
s_logger.debug("Can't find snapshot; deleting it in DB");
|
||||
|
||||
_snapshotDao.remove(snapshotId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
snapshotObj.processEvent(Snapshot.Event.DestroyRequested);
|
||||
}
|
||||
catch (NoTransitionException e) {
|
||||
s_logger.debug("Failed to set the state to destroying: ", e);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
snapshotSvr.deleteSnapshot(snapshotObj);
|
||||
|
||||
snapshotObj.processEvent(Snapshot.Event.OperationSucceeded);
|
||||
}
|
||||
catch (Exception e) {
|
||||
s_logger.debug("Failed to delete snapshot: ", e);
|
||||
|
||||
try {
|
||||
snapshotObj.processEvent(Snapshot.Event.OperationFailed);
|
||||
}
|
||||
catch (NoTransitionException e1) {
|
||||
s_logger.debug("Failed to change snapshot state: " + e.toString());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean revertSnapshot(Long snapshotId) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@DB
|
||||
public SnapshotInfo takeSnapshot(SnapshotInfo snapshotInfo) {
|
||||
SnapshotVO snapshotVO = _snapshotDao.acquireInLockTable(snapshotInfo.getId());
|
||||
|
||||
if (snapshotVO == null) {
|
||||
throw new CloudRuntimeException("Failed to acquire lock on the following snapshot: " + snapshotInfo.getId());
|
||||
}
|
||||
|
||||
try {
|
||||
VolumeInfo volumeInfo = snapshotInfo.getBaseVolume();
|
||||
|
||||
volumeInfo.stateTransit(Volume.Event.SnapshotRequested);
|
||||
|
||||
SnapshotResult result = null;
|
||||
|
||||
try {
|
||||
result = snapshotSvr.takeSnapshot(snapshotInfo);
|
||||
|
||||
if (result.isFailed()) {
|
||||
s_logger.debug("Failed to take the following snapshot: " + result.getResult());
|
||||
|
||||
throw new CloudRuntimeException(result.getResult());
|
||||
}
|
||||
|
||||
markAsBackedUp((SnapshotObject)result.getSnashot());
|
||||
}
|
||||
finally {
|
||||
if (result != null && result.isSuccess()) {
|
||||
volumeInfo.stateTransit(Volume.Event.OperationSucceeded);
|
||||
}
|
||||
else {
|
||||
volumeInfo.stateTransit(Volume.Event.OperationFailed);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (snapshotVO != null) {
|
||||
_snapshotDao.releaseFromLockTable(snapshotInfo.getId());
|
||||
}
|
||||
}
|
||||
|
||||
return snapshotInfo;
|
||||
}
|
||||
|
||||
private void markAsBackedUp(SnapshotObject snapshotObj) {
|
||||
try {
|
||||
snapshotObj.processEvent(Snapshot.Event.BackupToSecondary);
|
||||
snapshotObj.processEvent(Snapshot.Event.OperationSucceeded);
|
||||
}
|
||||
catch (NoTransitionException ex) {
|
||||
s_logger.debug("Failed to change state: " + ex.toString());
|
||||
|
||||
try {
|
||||
snapshotObj.processEvent(Snapshot.Event.OperationFailed);
|
||||
}
|
||||
catch (NoTransitionException ex2) {
|
||||
s_logger.debug("Failed to change state: " + ex2.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StrategyPriority canHandle(Snapshot snapshot, SnapshotOperation op) {
|
||||
long volumeId = snapshot.getVolumeId();
|
||||
VolumeVO volume = _volumeDao.findById(volumeId);
|
||||
|
||||
long storagePoolId = volume.getPoolId();
|
||||
DataStore dataStore = _dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary);
|
||||
|
||||
Map<String, String> mapCapabilities = dataStore.getDriver().getCapabilities();
|
||||
|
||||
String value = mapCapabilities.get(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString());
|
||||
Boolean supportsStorageSystemSnapshots = new Boolean(value);
|
||||
|
||||
if (supportsStorageSystemSnapshots) {
|
||||
return StrategyPriority.HIGHEST;
|
||||
}
|
||||
|
||||
return StrategyPriority.CANT_HANDLE;
|
||||
}
|
||||
}
|
||||
@ -17,6 +17,7 @@
|
||||
package org.apache.cloudstack.storage.datastore.driver;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ -25,6 +26,7 @@ import javax.inject.Inject;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
|
||||
@ -32,7 +34,6 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
|
||||
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
|
||||
import org.apache.cloudstack.storage.command.CommandResult;
|
||||
import org.apache.cloudstack.storage.command.CopyCmdAnswer;
|
||||
import org.apache.cloudstack.storage.command.CreateObjectAnswer;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO;
|
||||
@ -90,7 +91,11 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
|
||||
|
||||
@Override
|
||||
public Map<String, String> getCapabilities() {
|
||||
return null;
|
||||
Map<String, String> mapCapabilities = new HashMap<String, String>();
|
||||
|
||||
mapCapabilities.put(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString(), Boolean.TRUE.toString());
|
||||
|
||||
return mapCapabilities;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -473,28 +478,11 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
|
||||
|
||||
@Override
|
||||
public void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
|
||||
if (srcData.getType() == DataObjectType.SNAPSHOT && destData.getType() == DataObjectType.SNAPSHOT) {
|
||||
// in this situation, we don't want to copy the snapshot anywhere
|
||||
|
||||
CopyCmdAnswer copyCmdAnswer = new CopyCmdAnswer(destData.getTO());
|
||||
CopyCommandResult result = new CopyCommandResult(null, copyCmdAnswer);
|
||||
|
||||
result.setResult(null);
|
||||
|
||||
callback.complete(result);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canCopy(DataObject srcData, DataObject destData) {
|
||||
if (srcData.getType() == DataObjectType.SNAPSHOT && destData.getType() == DataObjectType.SNAPSHOT) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -595,6 +583,15 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
|
||||
SolidFireUtil.deleteSolidFireSnapshot(sfConnection, getSolidFireSnapshotId(snapshotInfo.getId()));
|
||||
|
||||
_snapshotDetailsDao.removeDetails(snapshotInfo.getId());
|
||||
|
||||
StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId);
|
||||
|
||||
// getUsedBytes(StoragePool) will not include the snapshot to delete because it has already been deleted by this point
|
||||
long usedBytes = getUsedBytes(storagePool);
|
||||
|
||||
storagePool.setUsedBytes(usedBytes < 0 ? 0 : usedBytes);
|
||||
|
||||
_storagePoolDao.update(storagePoolId, storagePool);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
s_logger.debug(SolidFireUtil.LOG_PREFIX + "Failed to delete SolidFire snapshot: " + snapshotInfo.getId(), ex);
|
||||
|
||||
@ -412,33 +412,47 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager,
|
||||
|
||||
// Verify parameters
|
||||
SnapshotVO snapshotCheck = _snapshotDao.findById(snapshotId);
|
||||
|
||||
if (snapshotCheck == null) {
|
||||
throw new InvalidParameterValueException("unable to find a snapshot with id " + snapshotId);
|
||||
}
|
||||
|
||||
_accountMgr.checkAccess(caller, null, true, snapshotCheck);
|
||||
|
||||
SnapshotStrategy snapshotStrategy = _storageStrategyFactory.getSnapshotStrategy(snapshotCheck, SnapshotOperation.DELETE);
|
||||
|
||||
if (snapshotStrategy == null) {
|
||||
s_logger.error("Unable to find snaphot strategy to handle snapshot with id '" + snapshotId + "'");
|
||||
|
||||
return false;
|
||||
}
|
||||
SnapshotDataStoreVO snapshotStoreRef = _snapshotStoreDao.findBySnapshot(snapshotId, DataStoreRole.Image);
|
||||
|
||||
try {
|
||||
boolean result = snapshotStrategy.deleteSnapshot(snapshotId);
|
||||
|
||||
if (result) {
|
||||
if (snapshotCheck.getState() == Snapshot.State.BackedUp) {
|
||||
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_DELETE, snapshotCheck.getAccountId(), snapshotCheck.getDataCenterId(), snapshotId,
|
||||
snapshotCheck.getName(), null, null, 0L, snapshotCheck.getClass().getName(), snapshotCheck.getUuid());
|
||||
}
|
||||
if (snapshotCheck.getState() != Snapshot.State.Error && snapshotCheck.getState() != Snapshot.State.Destroyed)
|
||||
_resourceLimitMgr.decrementResourceCount(snapshotCheck.getAccountId(), ResourceType.snapshot);
|
||||
if (snapshotCheck.getState() == Snapshot.State.BackedUp)
|
||||
_resourceLimitMgr.decrementResourceCount(snapshotCheck.getAccountId(), ResourceType.secondary_storage, new Long(snapshotStoreRef.getSize()));
|
||||
|
||||
if (snapshotCheck.getState() != Snapshot.State.Error && snapshotCheck.getState() != Snapshot.State.Destroyed) {
|
||||
_resourceLimitMgr.decrementResourceCount(snapshotCheck.getAccountId(), ResourceType.snapshot);
|
||||
}
|
||||
|
||||
if (snapshotCheck.getState() == Snapshot.State.BackedUp) {
|
||||
SnapshotDataStoreVO snapshotStoreRef = _snapshotStoreDao.findBySnapshot(snapshotId, DataStoreRole.Image);
|
||||
|
||||
if (snapshotStoreRef != null) {
|
||||
_resourceLimitMgr.decrementResourceCount(snapshotCheck.getAccountId(), ResourceType.secondary_storage, new Long(snapshotStoreRef.getSize()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
s_logger.debug("Failed to delete snapshot: " + snapshotCheck.getId() + ":" + e.toString());
|
||||
|
||||
throw new CloudRuntimeException("Failed to delete snapshot:" + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user