CLOUDSTACK-5863: revert volume snapshot for KVM/QCOW2

This commit is contained in:
Wei Zhou 2015-08-24 11:01:50 +02:00
parent e7ddbd4980
commit 92344c006d
23 changed files with 309 additions and 28 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

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

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

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

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'
},