Return-Path: X-Original-To: apmail-cloudstack-commits-archive@www.apache.org Delivered-To: apmail-cloudstack-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 846F117561 for ; Wed, 15 Oct 2014 04:05:10 +0000 (UTC) Received: (qmail 55814 invoked by uid 500); 15 Oct 2014 04:05:10 -0000 Delivered-To: apmail-cloudstack-commits-archive@cloudstack.apache.org Received: (qmail 55775 invoked by uid 500); 15 Oct 2014 04:05:10 -0000 Mailing-List: contact commits-help@cloudstack.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cloudstack.apache.org Delivered-To: mailing list commits@cloudstack.apache.org Received: (qmail 55766 invoked by uid 99); 15 Oct 2014 04:05:10 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 15 Oct 2014 04:05:10 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id E8A758A14BE; Wed, 15 Oct 2014 04:05:09 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: mtutkowski@apache.org To: commits@cloudstack.apache.org Message-Id: <0f315ae5f645440ab8c8c8883ed6ec79@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: git commit: updated refs/heads/master to e70dde0 Date: Wed, 15 Oct 2014 04:05:09 +0000 (UTC) Repository: cloudstack Updated Branches: refs/heads/master 340bcc60c -> e70dde09c Implementing snapshot-strategy support for reverting a volume to a snapshot Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/e70dde09 Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/e70dde09 Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/e70dde09 Branch: refs/heads/master Commit: e70dde09c919b4c64566dd84484b67208b2fd321 Parents: 340bcc6 Author: Mike Tutkowski Authored: Tue Oct 14 17:04:35 2014 -0600 Committer: Mike Tutkowski Committed: Tue Oct 14 22:04:18 2014 -0600 ---------------------------------------------------------------------- api/src/com/cloud/storage/Volume.java | 5 ++ .../snapshot/StorageSystemSnapshotStrategy.java | 85 +++++++++++++++++++- .../driver/SolidFirePrimaryDataStoreDriver.java | 27 ++++++- .../storage/datastore/util/SolidFireUtil.java | 43 ++++++++++ server/src/com/cloud/api/ApiResponseHelper.java | 38 ++++++++- 5 files changed, 190 insertions(+), 8 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cloudstack/blob/e70dde09/api/src/com/cloud/storage/Volume.java ---------------------------------------------------------------------- diff --git a/api/src/com/cloud/storage/Volume.java b/api/src/com/cloud/storage/Volume.java index b67a395..48145a7 100755 --- a/api/src/com/cloud/storage/Volume.java +++ b/api/src/com/cloud/storage/Volume.java @@ -38,6 +38,7 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba Ready("The volume is ready to be used."), Migrating("The volume is migrating to other storage pool"), Snapshotting("There is a snapshot created on this volume, not backed up to secondary storage yet"), + Reverting("Replace the existing volume on a storage system with a snapshot of it"), Resizing("The volume is being resized"), Expunging("The volume is being expunging"), Expunged("The volume is being expunging"), @@ -85,8 +86,11 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba s_fsm.addTransition(Expunging, Event.OperationSucceeded, Expunged); s_fsm.addTransition(Expunging, Event.OperationFailed, Destroy); s_fsm.addTransition(Ready, Event.SnapshotRequested, Snapshotting); + s_fsm.addTransition(Ready, Event.RevertRequested, Reverting); s_fsm.addTransition(Snapshotting, Event.OperationSucceeded, Ready); s_fsm.addTransition(Snapshotting, Event.OperationFailed, Ready); + s_fsm.addTransition(Reverting, Event.OperationSucceeded, Ready); + s_fsm.addTransition(Reverting, Event.OperationFailed, Ready); s_fsm.addTransition(Ready, Event.MigrationRequested, Migrating); s_fsm.addTransition(Migrating, Event.OperationSucceeded, Ready); s_fsm.addTransition(Migrating, Event.OperationFailed, Ready); @@ -111,6 +115,7 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba UploadRequested, MigrationRequested, SnapshotRequested, + RevertRequested, DestroyRequested, ExpungingRequested, ResizeRequested; http://git-wip-us.apache.org/repos/asf/cloudstack/blob/e70dde09/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/StorageSystemSnapshotStrategy.java ---------------------------------------------------------------------- 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 index 894aa5a..dc43d7d 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/StorageSystemSnapshotStrategy.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/StorageSystemSnapshotStrategy.java @@ -33,23 +33,35 @@ 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.dc.ClusterVO; +import com.cloud.dc.dao.ClusterDao; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.DataStoreRole; import com.cloud.storage.Snapshot; import com.cloud.storage.SnapshotVO; import com.cloud.storage.Volume; +import com.cloud.storage.Volume.Type; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.VolumeDao; +import com.cloud.uservm.UserVm; import com.cloud.utils.db.DB; +import com.cloud.utils.db.EntityManager; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.fsm.NoTransitionException; +import com.cloud.vm.VirtualMachine.State; @Component public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase { private static final Logger s_logger = Logger.getLogger(StorageSystemSnapshotStrategy.class); + @Inject private ClusterDao _clusterDao; @Inject private DataStoreManager _dataStoreMgr; + @Inject private EntityManager _entityMgr; + @Inject private HostDao _hostDao; @Inject private SnapshotDao _snapshotDao; @Inject private SnapshotDataFactory _snapshotDataFactory; @Inject private SnapshotDataStoreDao _snapshotStoreDao; @@ -121,6 +133,75 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase { @Override public boolean revertSnapshot(Long snapshotId) { + // verify the following: + // if root disk, the VM cannot be running (don't allow this at all for ESX) + // if data disk, the disk cannot be in the attached state + + SnapshotInfo snapshotInfo = _snapshotDataFactory.getSnapshot(snapshotId, DataStoreRole.Primary); + VolumeInfo volumeInfo = snapshotInfo.getBaseVolume(); + VolumeVO volume = _volumeDao.findById(volumeInfo.getId()); + + if (volume.getVolumeType() == Type.DATADISK) { + if (volume.getAttached() != null) { + throw new CloudRuntimeException("A data disk must be in the detached state in order to perform a revert."); + } + } + else if (volume.getVolumeType() == Type.ROOT) { + Long instanceId = volume.getInstanceId(); + UserVm vm = _entityMgr.findById(UserVm.class, instanceId); + + Long hostId = vm.getHostId(); + HostVO hostVO = _hostDao.findById(hostId); + Long clusterId = hostVO.getClusterId(); + ClusterVO clusterVO = _clusterDao.findById(clusterId); + + if (clusterVO.getHypervisorType() != HypervisorType.XenServer && clusterVO.getHypervisorType() != HypervisorType.KVM) { + throw new CloudRuntimeException("Unsupported hypervisor type for root disk revert. Create a template from this disk and use it instead."); + } + + if (vm.getState() != State.Stopped) { + throw new CloudRuntimeException("A root disk cannot be reverted unless the VM it's attached to is in the stopped state."); + } + } + else { + throw new CloudRuntimeException("Unsupported volume type"); + } + + SnapshotVO snapshotVO = _snapshotDao.acquireInLockTable(snapshotId); + + if (snapshotVO == null) { + throw new CloudRuntimeException("Failed to acquire lock on the following snapshot: " + snapshotId); + } + + try { + volumeInfo.stateTransit(Volume.Event.RevertRequested); + + boolean result = false; + + try { + result = snapshotSvr.revertSnapshot(snapshotId); + } + finally { + if (result) { + volumeInfo.stateTransit(Volume.Event.OperationSucceeded); + } + else { + String msg = "Failed to revert the volume to a snapshot"; + + s_logger.debug(msg); + + volumeInfo.stateTransit(Volume.Event.OperationFailed); + + throw new CloudRuntimeException("Failed to revert the volume to a snapshot"); + } + } + } + finally { + if (snapshotVO != null) { + _snapshotDao.releaseFromLockTable(snapshotId); + } + } + return true; } @@ -189,9 +270,9 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase { @Override public StrategyPriority canHandle(Snapshot snapshot, SnapshotOperation op) { long volumeId = snapshot.getVolumeId(); - VolumeVO volume = _volumeDao.findById(volumeId); + VolumeVO volumeVO = _volumeDao.findById(volumeId); - long storagePoolId = volume.getPoolId(); + long storagePoolId = volumeVO.getPoolId(); DataStore dataStore = _dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary); Map mapCapabilities = dataStore.getDriver().getCapabilities(); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/e70dde09/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java ---------------------------------------------------------------------- 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 946dd18..04970b7 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 @@ -609,8 +609,31 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { } @Override - public void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback callback) { - throw new UnsupportedOperationException(); + public void revertSnapshot(SnapshotInfo snapshotInfo, AsyncCompletionCallback callback) { + String errMsg = null; + + try { + VolumeInfo volumeInfo = snapshotInfo.getBaseVolume(); + + long storagePoolId = volumeInfo.getPoolId(); + long sfVolumeId = Long.parseLong(volumeInfo.getFolder()); + long sfSnapshotId = getSolidFireSnapshotId(snapshotInfo.getId()); + + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao); + + SolidFireUtil.rollBackVolumeToSnapshot(sfConnection, sfVolumeId, sfSnapshotId); + } + catch (Exception ex) { + s_logger.debug(SolidFireUtil.LOG_PREFIX + "Failed to take CloudStack snapshot: " + snapshotInfo.getId(), ex); + + errMsg = ex.getMessage(); + } + + CommandResult result = new CommandResult(); + + result.setResult(errMsg); + + callback.complete(result); } @Override http://git-wip-us.apache.org/repos/asf/cloudstack/blob/e70dde09/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java ---------------------------------------------------------------------- diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java index 65f3554..786f227 100644 --- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java @@ -689,6 +689,20 @@ public class SolidFireUtil { executeJsonRpc(sfConnection, strSnapshotToDeleteJson); } + public static void rollBackVolumeToSnapshot(SolidFireConnection sfConnection, long volumeId, long snapshotId) { + final Gson gson = new GsonBuilder().create(); + + RollbackToInitiate rollbackToInitiate = new RollbackToInitiate(volumeId, snapshotId); + + String strRollbackToInitiateJson = gson.toJson(rollbackToInitiate); + + String strRollbackInitiatedResultJson = executeJsonRpc(sfConnection, strRollbackToInitiateJson); + + RollbackInitiatedResult rollbackInitiatedResult = gson.fromJson(strRollbackInitiatedResultJson, RollbackInitiatedResult.class); + + verifyResult(rollbackInitiatedResult.result, strRollbackInitiatedResultJson, gson); + } + public static long createSolidFireClone(SolidFireConnection sfConnection, long lVolumeId, String cloneName) { final Gson gson = new GsonBuilder().create(); @@ -1294,6 +1308,26 @@ public class SolidFireUtil { } @SuppressWarnings("unused") + private static final class RollbackToInitiate { + private final String method = "RollbackToSnapshot"; + private final RollbackToInitiateParams params; + + private RollbackToInitiate(final long lVolumeId, final long lSnapshotId) { + params = new RollbackToInitiateParams(lVolumeId, lSnapshotId); + } + + private static final class RollbackToInitiateParams { + private long volumeID; + private long snapshotID; + + private RollbackToInitiateParams(final long lVolumeId, final long lSnapshotId) { + volumeID = lVolumeId; + snapshotID = lSnapshotId; + } + } + } + + @SuppressWarnings("unused") private static final class CloneToCreate { private final String method = "CloneVolume"; private final CloneToCreateParams params; @@ -1539,6 +1573,15 @@ public class SolidFireUtil { } } + @SuppressWarnings("unused") + private static final class RollbackInitiatedResult { + private Result result; + + private static final class Result { + private long snapshotID; + } + } + private static final class CloneCreateResult { private Result result; http://git-wip-us.apache.org/repos/asf/cloudstack/blob/e70dde09/server/src/com/cloud/api/ApiResponseHelper.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index f4d0293..50376d2 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -138,6 +138,9 @@ import org.apache.cloudstack.api.response.VpnUsersResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.config.Configuration; import org.apache.cloudstack.context.CallContext; +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.framework.jobs.AsyncJob; @@ -278,6 +281,7 @@ import com.cloud.storage.UploadVO; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.Volume; import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.snapshot.SnapshotPolicy; import com.cloud.storage.snapshot.SnapshotSchedule; import com.cloud.template.VirtualMachineTemplate; @@ -307,13 +311,13 @@ public class ApiResponseHelper implements ResponseGenerator { private static final Logger s_logger = Logger.getLogger(ApiResponseHelper.class); private static final DecimalFormat s_percentFormat = new DecimalFormat("##.##"); + @Inject private EntityManager _entityMgr; @Inject private UsageService _usageSvc; @Inject NetworkModel _ntwkModel; - @Inject protected AccountManager _accountMgr; @Inject @@ -322,6 +326,10 @@ public class ApiResponseHelper implements ResponseGenerator { ConfigurationManager _configMgr; @Inject SnapshotDataFactory snapshotfactory; + @Inject + private VolumeDao _volumeDao; + @Inject + private DataStoreManager _dataStoreMgr; @Override public UserResponse createUserResponse(User user) { @@ -459,10 +467,13 @@ public class ApiResponseHelper implements ResponseGenerator { snapshotResponse.setState(snapshot.getState()); SnapshotInfo snapshotInfo = null; - if (!(snapshot instanceof SnapshotInfo)) { - snapshotInfo = snapshotfactory.getSnapshot(snapshot.getId(), DataStoreRole.Image); - } else { + + if (snapshot instanceof SnapshotInfo) { snapshotInfo = (SnapshotInfo)snapshot; + } else { + DataStoreRole dataStoreRole = getDataStoreRole(snapshot); + + snapshotInfo = snapshotfactory.getSnapshot(snapshot.getId(), dataStoreRole); } if (snapshotInfo == null) { @@ -485,6 +496,25 @@ public class ApiResponseHelper implements ResponseGenerator { return snapshotResponse; } + private DataStoreRole getDataStoreRole(Snapshot snapshot) { + long volumeId = snapshot.getVolumeId(); + VolumeVO volumeVO = _volumeDao.findById(volumeId); + + long storagePoolId = volumeVO.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 DataStoreRole.Primary; + } + + return DataStoreRole.Image; + } + @Override public VMSnapshotResponse createVMSnapshotResponse(VMSnapshot vmSnapshot) { VMSnapshotResponse vmSnapshotResponse = new VMSnapshotResponse();