Merge pull request #732 from ustcweizhou/revert-volume-snapshot-master

Guys, can you review it? things need to be discussed:
(1) this supports KVM/QCOW2 only. Anyone want to implement for other Hypervisor/format ?
(2) The original data volume (on primary storage) will be removed.
(3) The script uses the default timeout in libvirtComputingResource. Do we need to add one in global configuration (like copy.volume.wait or backup.snapshot.wait, create.volume.from.snapshot.wait)
(4) In scripts/storage/qcow2/managesnapshot.sh, I use "qemu-img convert -f qcow2 -O qcow2" to copy the snapshot from secondary to primary (hence there is no base image file), instead of "cp -f", this is because convert is faster than cp in my testing.

* pr/732:
  CLOUDSTACK-5863: revert volume snapshot for KVM/QCOW2

Signed-off-by: Wei Zhou <w.zhou@tech.leaseweb.com>
This commit is contained in:
Wei Zhou 2015-09-01 16:18:19 +02:00
commit c0a0aec0f9
24 changed files with 315 additions and 33 deletions

View File

@ -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,

View File

@ -106,7 +106,7 @@ public interface SnapshotApiService {
*/
Long getHostIdForSnapshotOperation(Volume vol);
boolean revertSnapshot(Long snapshotId);
Snapshot revertSnapshot(Long snapshotId);
SnapshotPolicy updateSnapshotPolicy(UpdateSnapshotPolicyCmd updateSnapshotPolicyCmd);
}

View File

@ -29,8 +29,8 @@ import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
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;
@ -39,17 +39,27 @@ import com.cloud.user.Account;
@APICommand(name = "revertSnapshot", description = "revert a volume snapshot.", responseObject = SnapshotResponse.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;
@ -88,9 +98,9 @@ public class RevertSnapshotCmd extends BaseAsyncCmd {
@Override
public void execute() {
CallContext.current().setEventDetails("Snapshot Id: " + getId());
boolean result = _snapshotService.revertSnapshot(getId());
if (result) {
SuccessResponse response = new SuccessResponse(getCommandName());
Snapshot snapshot = _snapshotService.revertSnapshot(getId());
if (snapshot != null) {
SnapshotResponse response = _responseGenerator.createSnapshotResponse(snapshot);
response.setResponseName(getCommandName());
setResponseObject(response);
} else {

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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 {

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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");
}

View File

@ -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;
}
@ -291,6 +297,14 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
return findOneBy(sc);
}
@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();

View File

@ -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());
}
}
}

View File

@ -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();
}

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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.");
}

View File

@ -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

View File

@ -246,13 +246,17 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager,
}
@Override
public boolean revertSnapshot(Long snapshotId) {
Snapshot snapshot = _snapshotDao.findById(snapshotId);
public Snapshot revertSnapshot(Long 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,14 +268,28 @@ 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) {
s_logger.error("Unable to find snaphot strategy to handle snapshot with id '" + snapshotId + "'");
return false;
return null;
}
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 snapshotInfo;
}
return null;
}
@Override

View File

@ -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;

View File

@ -1862,6 +1862,9 @@
volumename: {
label: 'label.volume'
},
name: {
label: 'label.name'
},
intervaltype: {
label: 'label.interval.type'
},