mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
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:
commit
c0a0aec0f9
@ -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,
|
||||
|
||||
@ -106,7 +106,7 @@ public interface SnapshotApiService {
|
||||
*/
|
||||
Long getHostIdForSnapshotOperation(Volume vol);
|
||||
|
||||
boolean revertSnapshot(Long snapshotId);
|
||||
Snapshot revertSnapshot(Long snapshotId);
|
||||
|
||||
SnapshotPolicy updateSnapshotPolicy(UpdateSnapshotPolicyCmd updateSnapshotPolicyCmd);
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.");
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -1862,6 +1862,9 @@
|
||||
volumename: {
|
||||
label: 'label.volume'
|
||||
},
|
||||
name: {
|
||||
label: 'label.name'
|
||||
},
|
||||
intervaltype: {
|
||||
label: 'label.interval.type'
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user