Repository: cloudstack Updated Branches: refs/heads/master f732c7d1e -> c0a0aec0f
CLOUDSTACK-5863: revert volume snapshot for KVM/QCOW2 Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/92344c00 Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/92344c00 Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/92344c00 Branch: refs/heads/master Commit: 92344c006dfa8abf4ce81e2a25839cbb762d1b17 Parents: e7ddbd4 Author: Wei Zhou <[email protected]> Authored: Mon Aug 24 11:01:50 2015 +0200 Committer: Wei Zhou <[email protected]> Committed: Mon Aug 24 11:01:50 2015 +0200 ---------------------------------------------------------------------- api/src/com/cloud/storage/Volume.java | 5 ++ .../user/snapshot/RevertSnapshotCmd.java | 14 ++- .../storage/command/RevertSnapshotCommand.java | 49 ++++++++++ .../api/storage/PrimaryDataStoreDriver.java | 2 +- .../subsystem/api/storage/SnapshotService.java | 2 +- .../subsystem/api/storage/SnapshotStrategy.java | 2 +- .../datastore/db/SnapshotDataStoreDao.java | 3 + .../snapshot/SnapshotDataFactoryImpl.java | 5 +- .../storage/snapshot/SnapshotServiceImpl.java | 8 +- .../storage/snapshot/SnapshotStrategyBase.java | 4 +- .../snapshot/StorageSystemSnapshotStrategy.java | 54 +++++++++-- .../snapshot/XenserverSnapshotStrategy.java | 2 +- .../image/db/SnapshotDataStoreDaoImpl.java | 14 +++ .../LibvirtRevertSnapshotCommandWrapper.java | 95 ++++++++++++++++++++ .../ElastistorPrimaryDataStoreDriver.java | 2 +- .../CloudStackPrimaryDataStoreDriverImpl.java | 24 ++++- .../driver/NexentaPrimaryDataStoreDriver.java | 2 +- .../SamplePrimaryDataStoreDriverImpl.java | 2 +- .../driver/SolidFirePrimaryDataStoreDriver.java | 2 +- scripts/storage/qcow2/managesnapshot.sh | 18 +++- .../storage/snapshot/SnapshotManagerImpl.java | 23 ++++- ui/css/cloudstack3.css | 2 + ui/scripts/storage.js | 3 + 23 files changed, 309 insertions(+), 28 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cloudstack/blob/92344c00/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 9f5f502..f70ead9 100644 --- a/api/src/com/cloud/storage/Volume.java +++ b/api/src/com/cloud/storage/Volume.java @@ -39,6 +39,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"), + RevertSnapshotting("There is a snapshot created on this volume, the volume is being reverting from snapshot"), Resizing("The volume is being resized"), Expunging("The volume is being expunging"), Expunged("The volume has been expunged"), @@ -91,6 +92,9 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Ready, Event.SnapshotRequested, Snapshotting, null)); s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Snapshotting, Event.OperationSucceeded, Ready, null)); s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Snapshotting, Event.OperationFailed, Ready,null)); + s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Ready, Event.RevertSnapshotRequested, RevertSnapshotting, null)); + s_fsm.addTransition(new StateMachine2.Transition<State, Event>(RevertSnapshotting, Event.OperationSucceeded, Ready, null)); + s_fsm.addTransition(new StateMachine2.Transition<State, Event>(RevertSnapshotting, Event.OperationFailed, Ready,null)); s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Allocated, Event.MigrationCopyRequested, Creating, null)); s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Creating, Event.MigrationCopyFailed, Allocated, null)); s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Creating, Event.MigrationCopySucceeded, Ready, Arrays.asList(new StateMachine2.Transition.Impact[]{StateMachine2.Transition.Impact.USAGE}))); @@ -131,6 +135,7 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba MigrationCopySucceeded, MigrationCopyFailed, SnapshotRequested, + RevertSnapshotRequested, DestroyRequested, ExpungingRequested, ResizeRequested, http://git-wip-us.apache.org/repos/asf/cloudstack/blob/92344c00/api/src/org/apache/cloudstack/api/command/user/snapshot/RevertSnapshotCmd.java ---------------------------------------------------------------------- diff --git a/api/src/org/apache/cloudstack/api/command/user/snapshot/RevertSnapshotCmd.java b/api/src/org/apache/cloudstack/api/command/user/snapshot/RevertSnapshotCmd.java index 0c79f81..d2ccf6a 100644 --- a/api/src/org/apache/cloudstack/api/command/user/snapshot/RevertSnapshotCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/snapshot/RevertSnapshotCmd.java @@ -31,25 +31,36 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.SnapshotResponse; import org.apache.cloudstack.api.response.SuccessResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; import com.cloud.event.EventTypes; import com.cloud.storage.Snapshot; import com.cloud.user.Account; -@APICommand(name = "revertSnapshot", description = "revert a volume snapshot.", responseObject = SnapshotResponse.class, entityType = {Snapshot.class}, +@APICommand(name = "revertSnapshot", description = "revert a volume snapshot.", responseObject = SuccessResponse.class, entityType = {Snapshot.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class RevertSnapshotCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(RevertSnapshotCmd.class.getName()); private static final String s_name = "revertsnapshotresponse"; + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// @ACL(accessType = AccessType.OperateEntry) @Parameter(name= ApiConstants.ID, type= BaseCmd.CommandType.UUID, entityType = SnapshotResponse.class, required=true, description="The ID of the snapshot") private Long id; + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// public Long getId() { return id; } + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// @Override public String getCommandName() { return s_name; @@ -91,7 +102,6 @@ public class RevertSnapshotCmd extends BaseAsyncCmd { boolean result = _snapshotService.revertSnapshot(getId()); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); - response.setResponseName(getCommandName()); setResponseObject(response); } else { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to revert snapshot"); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/92344c00/core/src/org/apache/cloudstack/storage/command/RevertSnapshotCommand.java ---------------------------------------------------------------------- diff --git a/core/src/org/apache/cloudstack/storage/command/RevertSnapshotCommand.java b/core/src/org/apache/cloudstack/storage/command/RevertSnapshotCommand.java new file mode 100644 index 0000000..1a4403b --- /dev/null +++ b/core/src/org/apache/cloudstack/storage/command/RevertSnapshotCommand.java @@ -0,0 +1,49 @@ +/* + * 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.command; + +import org.apache.cloudstack.storage.to.SnapshotObjectTO; + +public final class RevertSnapshotCommand extends StorageSubSystemCommand { + private SnapshotObjectTO data; + private boolean _executeInSequence = false; + + public RevertSnapshotCommand(SnapshotObjectTO data) { + super(); + this.data = data; + } + + protected RevertSnapshotCommand() { + super(); + } + + public SnapshotObjectTO getData() { + return this.data; + } + + @Override + public void setExecuteInSequence(final boolean executeInSequence) { + _executeInSequence = executeInSequence; + } + + @Override + public boolean executeInSequence() { + return _executeInSequence; + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/92344c00/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java ---------------------------------------------------------------------- diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java index 5e7090d..0c6bd93 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java @@ -48,5 +48,5 @@ public interface PrimaryDataStoreDriver extends DataStoreDriver { public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback); - public void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CommandResult> callback); + public void revertSnapshot(SnapshotInfo snapshotOnImageStore, SnapshotInfo snapshotOnPrimaryStore, AsyncCompletionCallback<CommandResult> callback); } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/92344c00/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotService.java ---------------------------------------------------------------------- diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotService.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotService.java index 4edeb55..322de77 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotService.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotService.java @@ -24,7 +24,7 @@ public interface SnapshotService { boolean deleteSnapshot(SnapshotInfo snapshot); - boolean revertSnapshot(Long snapshotId); + boolean revertSnapshot(SnapshotInfo snapshot); void syncVolumeSnapshotsToRegionStore(long volumeId, DataStore store); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/92344c00/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotStrategy.java ---------------------------------------------------------------------- diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotStrategy.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotStrategy.java index 0af9b09..fbf6004 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotStrategy.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotStrategy.java @@ -29,7 +29,7 @@ public interface SnapshotStrategy { boolean deleteSnapshot(Long snapshotId); - boolean revertSnapshot(Long snapshotId); + boolean revertSnapshot(SnapshotInfo snapshot); StrategyPriority canHandle(Snapshot snapshot, SnapshotOperation op); } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/92344c00/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java ---------------------------------------------------------------------- diff --git a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java index 231b241..4b83e31 100644 --- a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java +++ b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java @@ -60,4 +60,7 @@ public interface SnapshotDataStoreDao extends GenericDao<SnapshotDataStoreVO, Lo SnapshotDataStoreVO findOldestSnapshotForVolume(Long volumeId, DataStoreRole role); void updateVolumeIds(long oldVolId, long newVolId); + + SnapshotDataStoreVO findByVolume(long volumeId, DataStoreRole role); + } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/92344c00/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotDataFactoryImpl.java ---------------------------------------------------------------------- diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotDataFactoryImpl.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotDataFactoryImpl.java index 16c14f3..171885e 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotDataFactoryImpl.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotDataFactoryImpl.java @@ -72,7 +72,10 @@ public class SnapshotDataFactoryImpl implements SnapshotDataFactory { SnapshotVO snapshot = snapshotDao.findById(snapshotId); SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findBySnapshot(snapshotId, role); if (snapshotStore == null) { - return null; + snapshotStore = snapshotStoreDao.findByVolume(snapshot.getVolumeId(), role); + if (snapshotStore == null) { + return null; + } } DataStore store = storeMgr.getDataStore(snapshotStore.getDataStoreId(), role); SnapshotObject so = SnapshotObject.getSnapshotObject(snapshot, store); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/92344c00/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java ---------------------------------------------------------------------- diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java index 4c76cda..a9aad0a 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java @@ -407,16 +407,16 @@ public class SnapshotServiceImpl implements SnapshotService { } @Override - public boolean revertSnapshot(Long snapshotId) { - SnapshotInfo snapshot = _snapshotFactory.getSnapshot(snapshotId, DataStoreRole.Primary); - PrimaryDataStore store = (PrimaryDataStore)snapshot.getDataStore(); + public boolean revertSnapshot(SnapshotInfo snapshot) { + SnapshotInfo snapshotOnPrimaryStore = _snapshotFactory.getSnapshot(snapshot.getId(), DataStoreRole.Primary); + PrimaryDataStore store = (PrimaryDataStore)snapshotOnPrimaryStore.getDataStore(); AsyncCallFuture<SnapshotResult> future = new AsyncCallFuture<SnapshotResult>(); RevertSnapshotContext<CommandResult> context = new RevertSnapshotContext<CommandResult>(null, snapshot, future); AsyncCallbackDispatcher<SnapshotServiceImpl, CommandResult> caller = AsyncCallbackDispatcher.create(this); caller.setCallback(caller.getTarget().revertSnapshotCallback(null, null)).setContext(context); - ((PrimaryDataStoreDriver)store.getDriver()).revertSnapshot(snapshot, caller); + ((PrimaryDataStoreDriver)store.getDriver()).revertSnapshot(snapshot, snapshotOnPrimaryStore, caller); SnapshotResult result = null; try { http://git-wip-us.apache.org/repos/asf/cloudstack/blob/92344c00/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotStrategyBase.java ---------------------------------------------------------------------- diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotStrategyBase.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotStrategyBase.java index 6db8343..b08a837 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotStrategyBase.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotStrategyBase.java @@ -37,7 +37,7 @@ public abstract class SnapshotStrategyBase implements SnapshotStrategy { } @Override - public boolean revertSnapshot(Long snapshotId) { - return snapshotSvr.revertSnapshot(snapshotId); + public boolean revertSnapshot(SnapshotInfo snapshot) { + return snapshotSvr.revertSnapshot(snapshot); } } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/92344c00/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 deec7fe..2c71525 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 @@ -35,6 +35,7 @@ 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.engine.subsystem.api.storage.VolumeService; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; import org.apache.cloudstack.storage.command.SnapshotAndCopyAnswer; import org.apache.cloudstack.storage.command.SnapshotAndCopyCommand; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; @@ -56,6 +57,8 @@ import com.cloud.storage.DataStoreRole; import com.cloud.storage.Snapshot; import com.cloud.storage.SnapshotVO; import com.cloud.storage.Storage.ImageFormat; +import com.cloud.storage.StoragePool; +import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.Volume; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.SnapshotDao; @@ -153,8 +156,44 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase { } @Override - public boolean revertSnapshot(Long snapshotId) { - throw new UnsupportedOperationException("Reverting not supported. Create a template or volume based on the snapshot instead."); + public boolean revertSnapshot(SnapshotInfo snapshot) { + if (canHandle(snapshot,SnapshotOperation.REVERT) == StrategyPriority.CANT_HANDLE) { + throw new UnsupportedOperationException("Reverting not supported. Create a template or volume based on the snapshot instead."); + } + + SnapshotVO snapshotVO = _snapshotDao.acquireInLockTable(snapshot.getId()); + if (snapshotVO == null) { + throw new CloudRuntimeException("Failed to get lock on snapshot:" + snapshot.getId()); + } + + try { + VolumeInfo volumeInfo = snapshot.getBaseVolume(); + StoragePool store = (StoragePool)volumeInfo.getDataStore(); + if (store != null && store.getStatus() != StoragePoolStatus.Up) { + snapshot.processEvent(Event.OperationFailed); + throw new CloudRuntimeException("store is not in up state"); + } + volumeInfo.stateTransit(Volume.Event.RevertSnapshotRequested); + boolean result = false; + try { + result = snapshotSvr.revertSnapshot(snapshot); + if (! result) { + s_logger.debug("Failed to revert snapshot: " + snapshot.getId()); + throw new CloudRuntimeException("Failed to revert snapshot: " + snapshot.getId()); + } + } finally { + if (result) { + volumeInfo.stateTransit(Volume.Event.OperationSucceeded); + } else { + volumeInfo.stateTransit(Volume.Event.OperationFailed); + } + } + return result; + } finally { + if (snapshotVO != null) { + _snapshotDao.releaseFromLockTable(snapshot.getId()); + } + } } @Override @@ -401,10 +440,6 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase { @Override public StrategyPriority canHandle(Snapshot snapshot, SnapshotOperation op) { - if (SnapshotOperation.REVERT.equals(op)) { - return StrategyPriority.CANT_HANDLE; - } - long volumeId = snapshot.getVolumeId(); VolumeVO volumeVO = _volumeDao.findById(volumeId); @@ -424,6 +459,13 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase { storagePoolId = volumeVO.getPoolId(); } + if (SnapshotOperation.REVERT.equals(op)) { + if (volumeVO != null && ImageFormat.QCOW2.equals(volumeVO.getFormat())) + return StrategyPriority.DEFAULT; + else + return StrategyPriority.CANT_HANDLE; + } + DataStore dataStore = _dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary); Map<String, String> mapCapabilities = dataStore.getDriver().getCapabilities(); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/92344c00/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java ---------------------------------------------------------------------- diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java index 8ed77df..96ea7bb 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java @@ -278,7 +278,7 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase { } @Override - public boolean revertSnapshot(Long snapshotId) { + public boolean revertSnapshot(SnapshotInfo snapshot) { throw new CloudRuntimeException("revert Snapshot is not supported"); } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/92344c00/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java ---------------------------------------------------------------------- diff --git a/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java b/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java index 142cd66..fea0b77 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java +++ b/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java @@ -55,6 +55,7 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO private SearchBuilder<SnapshotDataStoreVO> storeSnapshotSearch; private SearchBuilder<SnapshotDataStoreVO> snapshotIdSearch; private SearchBuilder<SnapshotDataStoreVO> volumeIdSearch; + private SearchBuilder<SnapshotDataStoreVO> volumeSearch; private final String parentSearch = "select store_id, store_role, snapshot_id from cloud.snapshot_store_ref where store_id = ? " + " and store_role = ? and volume_id = ? and state = 'Ready'" + " order by created DESC " + " limit 1"; @@ -119,6 +120,11 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO volumeIdSearch.and("volume_id", volumeIdSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); volumeIdSearch.done(); + volumeSearch = createSearchBuilder(); + volumeSearch.and("volume_id", volumeSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); + volumeSearch.and("store_role", volumeSearch.entity().getRole(), SearchCriteria.Op.EQ); + volumeSearch.done(); + return true; } @@ -292,6 +298,14 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO } @Override + public SnapshotDataStoreVO findByVolume(long volumeId, DataStoreRole role) { + SearchCriteria<SnapshotDataStoreVO> sc = volumeSearch.create(); + sc.setParameters("volume_id", volumeId); + sc.setParameters("store_role", role); + return findOneBy(sc); + } + + @Override public List<SnapshotDataStoreVO> findBySnapshotId(long snapshotId) { SearchCriteria<SnapshotDataStoreVO> sc = snapshotIdSearch.create(); sc.setParameters("snapshot_id", snapshotId); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/92344c00/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRevertSnapshotCommandWrapper.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRevertSnapshotCommandWrapper.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRevertSnapshotCommandWrapper.java new file mode 100644 index 0000000..22edf81 --- /dev/null +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRevertSnapshotCommandWrapper.java @@ -0,0 +1,95 @@ +// +// 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.hypervisor.kvm.resource.wrapper; + +import java.io.File; + +import org.apache.cloudstack.storage.command.RevertSnapshotCommand; +import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; +import org.apache.cloudstack.storage.to.SnapshotObjectTO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.NfsTO; +import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; +import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk; +import com.cloud.hypervisor.kvm.storage.KVMStoragePool; +import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager; +import com.cloud.resource.CommandWrapper; +import com.cloud.resource.ResourceWrapper; +import com.cloud.storage.Storage.StoragePoolType; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; + +@ResourceWrapper(handles = RevertSnapshotCommand.class) +public final class LibvirtRevertSnapshotCommandWrapper extends CommandWrapper<RevertSnapshotCommand, Answer, LibvirtComputingResource> { + + private static final Logger s_logger = Logger.getLogger(LibvirtRevertSnapshotCommandWrapper.class); + + @Override + public Answer execute(final RevertSnapshotCommand command, final LibvirtComputingResource libvirtComputingResource) { + SnapshotObjectTO snapshot = command.getData(); + VolumeObjectTO volume = snapshot.getVolume(); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO) volume.getDataStore(); + DataStoreTO snapshotImageStore = snapshot.getDataStore(); + if (!(snapshotImageStore instanceof NfsTO)) { + return new Answer(command, false, "revert snapshot on object storage is not implemented yet"); + } + NfsTO nfsImageStore = (NfsTO) snapshotImageStore; + + String secondaryStoragePoolUrl = nfsImageStore.getUrl(); + + String volumePath = volume.getPath(); + String snapshotPath = null; + String snapshotRelPath = null; + KVMStoragePool secondaryStoragePool = null; + try { + final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr(); + secondaryStoragePool = storagePoolMgr.getStoragePoolByURI(secondaryStoragePoolUrl); + String ssPmountPath = secondaryStoragePool.getLocalPath(); + snapshotRelPath = snapshot.getPath(); + snapshotPath = ssPmountPath + File.separator + snapshotRelPath; + + KVMPhysicalDisk snapshotDisk = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), + primaryStore.getUuid(), volumePath); + KVMStoragePool primaryPool = snapshotDisk.getPool(); + + if (primaryPool.getType() == StoragePoolType.RBD) { + return new Answer(command, false, "revert snapshot to RBD is not implemented yet"); + } else { + Script cmd = new Script(libvirtComputingResource.manageSnapshotPath(), libvirtComputingResource.getCmdsTimeout(), s_logger); + cmd.add("-v", snapshotPath); + cmd.add("-n", snapshotDisk.getName()); + cmd.add("-p", snapshotDisk.getPath()); + String result = cmd.execute(); + if (result != null) { + s_logger.debug("Failed to revert snaptshot: " + result); + return new Answer(command, false, result); + } + } + + return new Answer(command, true, "RevertSnapshotCommand executes successfully"); + } catch (CloudRuntimeException e) { + return new Answer(command, false, e.toString()); + } + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/92344c00/plugins/storage/volume/cloudbyte/src/org/apache/cloudstack/storage/datastore/driver/ElastistorPrimaryDataStoreDriver.java ---------------------------------------------------------------------- diff --git a/plugins/storage/volume/cloudbyte/src/org/apache/cloudstack/storage/datastore/driver/ElastistorPrimaryDataStoreDriver.java b/plugins/storage/volume/cloudbyte/src/org/apache/cloudstack/storage/datastore/driver/ElastistorPrimaryDataStoreDriver.java index cc7c8ef..b7ae4d4 100644 --- a/plugins/storage/volume/cloudbyte/src/org/apache/cloudstack/storage/datastore/driver/ElastistorPrimaryDataStoreDriver.java +++ b/plugins/storage/volume/cloudbyte/src/org/apache/cloudstack/storage/datastore/driver/ElastistorPrimaryDataStoreDriver.java @@ -393,7 +393,7 @@ public class ElastistorPrimaryDataStoreDriver extends CloudStackPrimaryDataStore } @Override - public void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CommandResult> callback) { + public void revertSnapshot(SnapshotInfo snapshot, SnapshotInfo snapshotOnPrimaryStore, AsyncCompletionCallback<CommandResult> callback) { throw new UnsupportedOperationException(); } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/92344c00/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java ---------------------------------------------------------------------- diff --git a/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java b/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java index e416bf8..e92e8f6 100644 --- a/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java @@ -46,6 +46,7 @@ import org.apache.cloudstack.storage.command.CopyCmdAnswer; import org.apache.cloudstack.storage.command.CopyCommand; import org.apache.cloudstack.storage.command.CreateObjectCommand; import org.apache.cloudstack.storage.command.DeleteCommand; +import org.apache.cloudstack.storage.command.RevertSnapshotCommand; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.to.SnapshotObjectTO; @@ -323,7 +324,28 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri } @Override - public void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CommandResult> callback) { + public void revertSnapshot(SnapshotInfo snapshot, SnapshotInfo snapshotOnPrimaryStore, AsyncCompletionCallback<CommandResult> callback) { + SnapshotObjectTO snapshotTO = (SnapshotObjectTO)snapshot.getTO(); + RevertSnapshotCommand cmd = new RevertSnapshotCommand(snapshotTO); + + CommandResult result = new CommandResult(); + try { + EndPoint ep = epSelector.select(snapshotOnPrimaryStore); + if ( ep == null ){ + String errMsg = "No remote endpoint to send RevertSnapshotCommand, check if host or ssvm is down?"; + s_logger.error(errMsg); + result.setResult(errMsg); + } else { + Answer answer = ep.sendMessage(cmd); + if (answer != null && !answer.getResult()) { + result.setResult(answer.getDetails()); + } + } + } catch (Exception ex) { + s_logger.debug("Unable to revert snapshot " + snapshot.getId(), ex); + result.setResult(ex.toString()); + } + callback.complete(result); } @Override http://git-wip-us.apache.org/repos/asf/cloudstack/blob/92344c00/plugins/storage/volume/nexenta/src/org/apache/cloudstack/storage/datastore/driver/NexentaPrimaryDataStoreDriver.java ---------------------------------------------------------------------- diff --git a/plugins/storage/volume/nexenta/src/org/apache/cloudstack/storage/datastore/driver/NexentaPrimaryDataStoreDriver.java b/plugins/storage/volume/nexenta/src/org/apache/cloudstack/storage/datastore/driver/NexentaPrimaryDataStoreDriver.java index 7ce46a2..7524703 100644 --- a/plugins/storage/volume/nexenta/src/org/apache/cloudstack/storage/datastore/driver/NexentaPrimaryDataStoreDriver.java +++ b/plugins/storage/volume/nexenta/src/org/apache/cloudstack/storage/datastore/driver/NexentaPrimaryDataStoreDriver.java @@ -130,7 +130,7 @@ public class NexentaPrimaryDataStoreDriver implements PrimaryDataStoreDriver { public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback) {} @Override - public void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CommandResult> callback) {} + public void revertSnapshot(SnapshotInfo snapshot, SnapshotInfo snapshotOnPrimaryStore, AsyncCompletionCallback<CommandResult> callback) {} @Override public void createAsync(DataStore dataStore, DataObject dataObject, AsyncCompletionCallback<CreateCmdResult> callback) { http://git-wip-us.apache.org/repos/asf/cloudstack/blob/92344c00/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java ---------------------------------------------------------------------- diff --git a/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java b/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java index 7c989d2..0a4cfd6 100644 --- a/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java @@ -210,7 +210,7 @@ public class SamplePrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver } @Override - public void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CommandResult> callback) { + public void revertSnapshot(SnapshotInfo snapshot, SnapshotInfo snapshotOnPrimaryStore, AsyncCompletionCallback<CommandResult> callback) { } @Override http://git-wip-us.apache.org/repos/asf/cloudstack/blob/92344c00/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 a19aa6b..e934426 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 @@ -688,7 +688,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { } @Override - public void revertSnapshot(SnapshotInfo snapshotInfo, AsyncCompletionCallback<CommandResult> callback) { + public void revertSnapshot(SnapshotInfo snapshot, SnapshotInfo snapshotOnPrimaryStore, AsyncCompletionCallback<CommandResult> callback) { throw new UnsupportedOperationException("Reverting not supported. Create a template or volume based on the snapshot instead."); } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/92344c00/scripts/storage/qcow2/managesnapshot.sh ---------------------------------------------------------------------- diff --git a/scripts/storage/qcow2/managesnapshot.sh b/scripts/storage/qcow2/managesnapshot.sh index 4225407..b8caa2e 100755 --- a/scripts/storage/qcow2/managesnapshot.sh +++ b/scripts/storage/qcow2/managesnapshot.sh @@ -235,12 +235,21 @@ backup_snapshot() { fi return 0 } + +revert_snapshot() { + local snapshotPath=$1 + local destPath=$2 + ${qemu_img} convert -f qcow2 -O qcow2 "$snapshotPath" "$destPath" || \ + ( printf "${qemu_img} failed to revert snapshot ${snapshotPath} to disk ${destPath}.\n" >&2; return 2 ) + return 0 +} #set -x cflag= dflag= rflag= bflag= +vflag= nflag= pathval= snapshot= @@ -249,7 +258,7 @@ deleteDir= dmsnapshot=no dmrollback=no -while getopts 'c:d:r:n:b:p:t:f' OPTION +while getopts 'c:d:r:n:b:v:p:t:f' OPTION do case $OPTION in c) cflag=1 @@ -264,6 +273,9 @@ do b) bflag=1 pathval="$OPTARG" ;; + v) vflag=1 + pathval="$OPTARG" + ;; n) nflag=1 snapshot="$OPTARG" ;; @@ -304,6 +316,10 @@ elif [ "$rflag" == "1" ] then rollback_snapshot $pathval "$snapshot" $destPath exit $? +elif [ "$vflag" == "1" ] +then + revert_snapshot $pathval $destPath + exit $? fi http://git-wip-us.apache.org/repos/asf/cloudstack/blob/92344c00/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java index bc66130..d6fafb1 100644 --- a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -247,12 +247,16 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, @Override public boolean revertSnapshot(Long snapshotId) { - Snapshot snapshot = _snapshotDao.findById(snapshotId); + SnapshotVO snapshot = _snapshotDao.findById(snapshotId); if (snapshot == null) { throw new InvalidParameterValueException("No such snapshot"); } - Volume volume = _volsDao.findById(snapshot.getVolumeId()); + VolumeVO volume = _volsDao.findById(snapshot.getVolumeId()); + if (volume.getState() != Volume.State.Ready) { + throw new InvalidParameterValueException("The volume is not in Ready state."); + } + Long instanceId = volume.getInstanceId(); // If this volume is attached to an VM, then the VM needs to be in the stopped state @@ -264,6 +268,11 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, } } + SnapshotInfo snapshotInfo = snapshotFactory.getSnapshot(snapshotId, DataStoreRole.Image); + if (snapshotInfo == null) { + throw new CloudRuntimeException("snapshot:" + snapshotId + " not exist in data store"); + } + SnapshotStrategy snapshotStrategy = _storageStrategyFactory.getSnapshotStrategy(snapshot, SnapshotOperation.REVERT); if (snapshotStrategy == null) { @@ -271,7 +280,15 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, return false; } - return snapshotStrategy.revertSnapshot(snapshotId); + boolean result = snapshotStrategy.revertSnapshot(snapshotInfo); + if (result) { + // update volume size and primary storage count + _resourceLimitMgr.decrementResourceCount(snapshot.getAccountId(), ResourceType.primary_storage, + new Long(volume.getSize() - snapshot.getSize())); + volume.setSize(snapshot.getSize()); + _volsDao.update(volume.getId(), volume); + } + return result; } @Override http://git-wip-us.apache.org/repos/asf/cloudstack/blob/92344c00/ui/css/cloudstack3.css ---------------------------------------------------------------------- diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 60ac9ca..c170bde 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -12675,6 +12675,7 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it background-position: -167px -677px; } +.revertSnapshot .icon, .restoreVM .icon, .restore .icon, .recover .icon { @@ -12690,6 +12691,7 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it background-position: -167px -66px; } +.revertSnapshot:hover .icon, .restoreVM:hover .icon, .restore:hover .icon { background-position: -168px -613px; http://git-wip-us.apache.org/repos/asf/cloudstack/blob/92344c00/ui/scripts/storage.js ---------------------------------------------------------------------- diff --git a/ui/scripts/storage.js b/ui/scripts/storage.js index ce4f077..77bd002 100644 --- a/ui/scripts/storage.js +++ b/ui/scripts/storage.js @@ -1862,6 +1862,9 @@ volumename: { label: 'label.volume' }, + name: { + label: 'label.name' + }, intervaltype: { label: 'label.interval.type' },
