diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreCapabilities.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreCapabilities.java
index 25c66cdcfb4..09883c66ce7 100644
--- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreCapabilities.java
+++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreCapabilities.java
@@ -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
}
diff --git a/engine/storage/snapshot/resources/META-INF/cloudstack/storage/spring-engine-storage-snapshot-storage-context.xml b/engine/storage/snapshot/resources/META-INF/cloudstack/storage/spring-engine-storage-snapshot-storage-context.xml
index 72997509130..de8d11ed372 100644
--- a/engine/storage/snapshot/resources/META-INF/cloudstack/storage/spring-engine-storage-snapshot-storage-context.xml
+++ b/engine/storage/snapshot/resources/META-INF/cloudstack/storage/spring-engine-storage-snapshot-storage-context.xml
@@ -30,6 +30,9 @@
+
+
diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/StorageSystemSnapshotStrategy.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/StorageSystemSnapshotStrategy.java
new file mode 100644
index 00000000000..894aa5a1c9a
--- /dev/null
+++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/StorageSystemSnapshotStrategy.java
@@ -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 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;
+ }
+}
diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java
index e966cf6e061..946dd18830a 100644
--- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java
+++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java
@@ -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 getCapabilities() {
- return null;
+ Map mapCapabilities = new HashMap();
+
+ 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 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);
diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java
index 76ab370ae8b..f2126efbf74 100755
--- a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java
+++ b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java
@@ -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());
}
}