mirror of https://github.com/apache/cloudstack.git
CLOUDSTACK-5863: revert volume snapshot for KVM/QCOW2
This commit is contained in:
parent
e7ddbd4980
commit
92344c006d
|
|
@ -39,6 +39,7 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba
|
||||||
Ready("The volume is ready to be used."),
|
Ready("The volume is ready to be used."),
|
||||||
Migrating("The volume is migrating to other storage pool"),
|
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"),
|
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"),
|
Resizing("The volume is being resized"),
|
||||||
Expunging("The volume is being expunging"),
|
Expunging("The volume is being expunging"),
|
||||||
Expunged("The volume has been expunged"),
|
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>(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.OperationSucceeded, Ready, null));
|
||||||
s_fsm.addTransition(new StateMachine2.Transition<State, Event>(Snapshotting, Event.OperationFailed, 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>(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.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})));
|
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,
|
MigrationCopySucceeded,
|
||||||
MigrationCopyFailed,
|
MigrationCopyFailed,
|
||||||
SnapshotRequested,
|
SnapshotRequested,
|
||||||
|
RevertSnapshotRequested,
|
||||||
DestroyRequested,
|
DestroyRequested,
|
||||||
ExpungingRequested,
|
ExpungingRequested,
|
||||||
ResizeRequested,
|
ResizeRequested,
|
||||||
|
|
|
||||||
|
|
@ -31,25 +31,36 @@ import org.apache.cloudstack.api.ServerApiException;
|
||||||
import org.apache.cloudstack.api.response.SnapshotResponse;
|
import org.apache.cloudstack.api.response.SnapshotResponse;
|
||||||
import org.apache.cloudstack.api.response.SuccessResponse;
|
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||||
import org.apache.cloudstack.context.CallContext;
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
import com.cloud.event.EventTypes;
|
import com.cloud.event.EventTypes;
|
||||||
import com.cloud.storage.Snapshot;
|
import com.cloud.storage.Snapshot;
|
||||||
import com.cloud.user.Account;
|
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)
|
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
||||||
public class RevertSnapshotCmd extends BaseAsyncCmd {
|
public class RevertSnapshotCmd extends BaseAsyncCmd {
|
||||||
|
public static final Logger s_logger = Logger.getLogger(RevertSnapshotCmd.class.getName());
|
||||||
private static final String s_name = "revertsnapshotresponse";
|
private static final String s_name = "revertsnapshotresponse";
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
//////////////// API parameters /////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
@ACL(accessType = AccessType.OperateEntry)
|
@ACL(accessType = AccessType.OperateEntry)
|
||||||
@Parameter(name= ApiConstants.ID, type= BaseCmd.CommandType.UUID, entityType = SnapshotResponse.class,
|
@Parameter(name= ApiConstants.ID, type= BaseCmd.CommandType.UUID, entityType = SnapshotResponse.class,
|
||||||
required=true, description="The ID of the snapshot")
|
required=true, description="The ID of the snapshot")
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////////// Accessors ///////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
public Long getId() {
|
public Long getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////// API Implementation///////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
@Override
|
@Override
|
||||||
public String getCommandName() {
|
public String getCommandName() {
|
||||||
return s_name;
|
return s_name;
|
||||||
|
|
@ -91,7 +102,6 @@ public class RevertSnapshotCmd extends BaseAsyncCmd {
|
||||||
boolean result = _snapshotService.revertSnapshot(getId());
|
boolean result = _snapshotService.revertSnapshot(getId());
|
||||||
if (result) {
|
if (result) {
|
||||||
SuccessResponse response = new SuccessResponse(getCommandName());
|
SuccessResponse response = new SuccessResponse(getCommandName());
|
||||||
response.setResponseName(getCommandName());
|
|
||||||
setResponseObject(response);
|
setResponseObject(response);
|
||||||
} else {
|
} else {
|
||||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to revert snapshot");
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to revert snapshot");
|
||||||
|
|
|
||||||
|
|
@ -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 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 deleteSnapshot(SnapshotInfo snapshot);
|
||||||
|
|
||||||
boolean revertSnapshot(Long snapshotId);
|
boolean revertSnapshot(SnapshotInfo snapshot);
|
||||||
|
|
||||||
void syncVolumeSnapshotsToRegionStore(long volumeId, DataStore store);
|
void syncVolumeSnapshotsToRegionStore(long volumeId, DataStore store);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ public interface SnapshotStrategy {
|
||||||
|
|
||||||
boolean deleteSnapshot(Long snapshotId);
|
boolean deleteSnapshot(Long snapshotId);
|
||||||
|
|
||||||
boolean revertSnapshot(Long snapshotId);
|
boolean revertSnapshot(SnapshotInfo snapshot);
|
||||||
|
|
||||||
StrategyPriority canHandle(Snapshot snapshot, SnapshotOperation op);
|
StrategyPriority canHandle(Snapshot snapshot, SnapshotOperation op);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,4 +60,7 @@ public interface SnapshotDataStoreDao extends GenericDao<SnapshotDataStoreVO, Lo
|
||||||
SnapshotDataStoreVO findOldestSnapshotForVolume(Long volumeId, DataStoreRole role);
|
SnapshotDataStoreVO findOldestSnapshotForVolume(Long volumeId, DataStoreRole role);
|
||||||
|
|
||||||
void updateVolumeIds(long oldVolId, long newVolId);
|
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);
|
SnapshotVO snapshot = snapshotDao.findById(snapshotId);
|
||||||
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findBySnapshot(snapshotId, role);
|
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findBySnapshot(snapshotId, role);
|
||||||
if (snapshotStore == null) {
|
if (snapshotStore == null) {
|
||||||
return null;
|
snapshotStore = snapshotStoreDao.findByVolume(snapshot.getVolumeId(), role);
|
||||||
|
if (snapshotStore == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
DataStore store = storeMgr.getDataStore(snapshotStore.getDataStoreId(), role);
|
DataStore store = storeMgr.getDataStore(snapshotStore.getDataStoreId(), role);
|
||||||
SnapshotObject so = SnapshotObject.getSnapshotObject(snapshot, store);
|
SnapshotObject so = SnapshotObject.getSnapshotObject(snapshot, store);
|
||||||
|
|
|
||||||
|
|
@ -407,16 +407,16 @@ public class SnapshotServiceImpl implements SnapshotService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean revertSnapshot(Long snapshotId) {
|
public boolean revertSnapshot(SnapshotInfo snapshot) {
|
||||||
SnapshotInfo snapshot = _snapshotFactory.getSnapshot(snapshotId, DataStoreRole.Primary);
|
SnapshotInfo snapshotOnPrimaryStore = _snapshotFactory.getSnapshot(snapshot.getId(), DataStoreRole.Primary);
|
||||||
PrimaryDataStore store = (PrimaryDataStore)snapshot.getDataStore();
|
PrimaryDataStore store = (PrimaryDataStore)snapshotOnPrimaryStore.getDataStore();
|
||||||
|
|
||||||
AsyncCallFuture<SnapshotResult> future = new AsyncCallFuture<SnapshotResult>();
|
AsyncCallFuture<SnapshotResult> future = new AsyncCallFuture<SnapshotResult>();
|
||||||
RevertSnapshotContext<CommandResult> context = new RevertSnapshotContext<CommandResult>(null, snapshot, future);
|
RevertSnapshotContext<CommandResult> context = new RevertSnapshotContext<CommandResult>(null, snapshot, future);
|
||||||
AsyncCallbackDispatcher<SnapshotServiceImpl, CommandResult> caller = AsyncCallbackDispatcher.create(this);
|
AsyncCallbackDispatcher<SnapshotServiceImpl, CommandResult> caller = AsyncCallbackDispatcher.create(this);
|
||||||
caller.setCallback(caller.getTarget().revertSnapshotCallback(null, null)).setContext(context);
|
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;
|
SnapshotResult result = null;
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ public abstract class SnapshotStrategyBase implements SnapshotStrategy {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean revertSnapshot(Long snapshotId) {
|
public boolean revertSnapshot(SnapshotInfo snapshot) {
|
||||||
return snapshotSvr.revertSnapshot(snapshotId);
|
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.StrategyPriority;
|
||||||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
|
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.VolumeService;
|
||||||
|
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event;
|
||||||
import org.apache.cloudstack.storage.command.SnapshotAndCopyAnswer;
|
import org.apache.cloudstack.storage.command.SnapshotAndCopyAnswer;
|
||||||
import org.apache.cloudstack.storage.command.SnapshotAndCopyCommand;
|
import org.apache.cloudstack.storage.command.SnapshotAndCopyCommand;
|
||||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
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.Snapshot;
|
||||||
import com.cloud.storage.SnapshotVO;
|
import com.cloud.storage.SnapshotVO;
|
||||||
import com.cloud.storage.Storage.ImageFormat;
|
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.Volume;
|
||||||
import com.cloud.storage.VolumeVO;
|
import com.cloud.storage.VolumeVO;
|
||||||
import com.cloud.storage.dao.SnapshotDao;
|
import com.cloud.storage.dao.SnapshotDao;
|
||||||
|
|
@ -153,8 +156,44 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean revertSnapshot(Long snapshotId) {
|
public boolean revertSnapshot(SnapshotInfo snapshot) {
|
||||||
throw new UnsupportedOperationException("Reverting not supported. Create a template or volume based on the snapshot instead.");
|
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
|
@Override
|
||||||
|
|
@ -401,10 +440,6 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StrategyPriority canHandle(Snapshot snapshot, SnapshotOperation op) {
|
public StrategyPriority canHandle(Snapshot snapshot, SnapshotOperation op) {
|
||||||
if (SnapshotOperation.REVERT.equals(op)) {
|
|
||||||
return StrategyPriority.CANT_HANDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
long volumeId = snapshot.getVolumeId();
|
long volumeId = snapshot.getVolumeId();
|
||||||
VolumeVO volumeVO = _volumeDao.findById(volumeId);
|
VolumeVO volumeVO = _volumeDao.findById(volumeId);
|
||||||
|
|
||||||
|
|
@ -424,6 +459,13 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
|
||||||
storagePoolId = volumeVO.getPoolId();
|
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);
|
DataStore dataStore = _dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary);
|
||||||
|
|
||||||
Map<String, String> mapCapabilities = dataStore.getDriver().getCapabilities();
|
Map<String, String> mapCapabilities = dataStore.getDriver().getCapabilities();
|
||||||
|
|
|
||||||
|
|
@ -278,7 +278,7 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean revertSnapshot(Long snapshotId) {
|
public boolean revertSnapshot(SnapshotInfo snapshot) {
|
||||||
throw new CloudRuntimeException("revert Snapshot is not supported");
|
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> storeSnapshotSearch;
|
||||||
private SearchBuilder<SnapshotDataStoreVO> snapshotIdSearch;
|
private SearchBuilder<SnapshotDataStoreVO> snapshotIdSearch;
|
||||||
private SearchBuilder<SnapshotDataStoreVO> volumeIdSearch;
|
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 = ? "
|
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";
|
+ " 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.and("volume_id", volumeIdSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
|
||||||
volumeIdSearch.done();
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -291,6 +297,14 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
|
||||||
return findOneBy(sc);
|
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
|
@Override
|
||||||
public List<SnapshotDataStoreVO> findBySnapshotId(long snapshotId) {
|
public List<SnapshotDataStoreVO> findBySnapshotId(long snapshotId) {
|
||||||
SearchCriteria<SnapshotDataStoreVO> sc = snapshotIdSearch.create();
|
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
|
@Override
|
||||||
public void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CommandResult> callback) {
|
public void revertSnapshot(SnapshotInfo snapshot, SnapshotInfo snapshotOnPrimaryStore, AsyncCompletionCallback<CommandResult> callback) {
|
||||||
throw new UnsupportedOperationException();
|
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.CopyCommand;
|
||||||
import org.apache.cloudstack.storage.command.CreateObjectCommand;
|
import org.apache.cloudstack.storage.command.CreateObjectCommand;
|
||||||
import org.apache.cloudstack.storage.command.DeleteCommand;
|
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.PrimaryDataStoreDao;
|
||||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||||
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
|
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
|
||||||
|
|
@ -323,7 +324,28 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ public class NexentaPrimaryDataStoreDriver implements PrimaryDataStoreDriver {
|
||||||
public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback) {}
|
public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback) {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CommandResult> callback) {}
|
public void revertSnapshot(SnapshotInfo snapshot, SnapshotInfo snapshotOnPrimaryStore, AsyncCompletionCallback<CommandResult> callback) {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createAsync(DataStore dataStore, DataObject dataObject, AsyncCompletionCallback<CreateCmdResult> callback) {
|
public void createAsync(DataStore dataStore, DataObject dataObject, AsyncCompletionCallback<CreateCmdResult> callback) {
|
||||||
|
|
|
||||||
|
|
@ -210,7 +210,7 @@ public class SamplePrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CommandResult> callback) {
|
public void revertSnapshot(SnapshotInfo snapshot, SnapshotInfo snapshotOnPrimaryStore, AsyncCompletionCallback<CommandResult> callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -688,7 +688,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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.");
|
throw new UnsupportedOperationException("Reverting not supported. Create a template or volume based on the snapshot instead.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -235,12 +235,21 @@ backup_snapshot() {
|
||||||
fi
|
fi
|
||||||
return 0
|
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
|
#set -x
|
||||||
|
|
||||||
cflag=
|
cflag=
|
||||||
dflag=
|
dflag=
|
||||||
rflag=
|
rflag=
|
||||||
bflag=
|
bflag=
|
||||||
|
vflag=
|
||||||
nflag=
|
nflag=
|
||||||
pathval=
|
pathval=
|
||||||
snapshot=
|
snapshot=
|
||||||
|
|
@ -249,7 +258,7 @@ deleteDir=
|
||||||
dmsnapshot=no
|
dmsnapshot=no
|
||||||
dmrollback=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
|
do
|
||||||
case $OPTION in
|
case $OPTION in
|
||||||
c) cflag=1
|
c) cflag=1
|
||||||
|
|
@ -264,6 +273,9 @@ do
|
||||||
b) bflag=1
|
b) bflag=1
|
||||||
pathval="$OPTARG"
|
pathval="$OPTARG"
|
||||||
;;
|
;;
|
||||||
|
v) vflag=1
|
||||||
|
pathval="$OPTARG"
|
||||||
|
;;
|
||||||
n) nflag=1
|
n) nflag=1
|
||||||
snapshot="$OPTARG"
|
snapshot="$OPTARG"
|
||||||
;;
|
;;
|
||||||
|
|
@ -304,6 +316,10 @@ elif [ "$rflag" == "1" ]
|
||||||
then
|
then
|
||||||
rollback_snapshot $pathval "$snapshot" $destPath
|
rollback_snapshot $pathval "$snapshot" $destPath
|
||||||
exit $?
|
exit $?
|
||||||
|
elif [ "$vflag" == "1" ]
|
||||||
|
then
|
||||||
|
revert_snapshot $pathval $destPath
|
||||||
|
exit $?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -247,12 +247,16 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager,
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean revertSnapshot(Long snapshotId) {
|
public boolean revertSnapshot(Long snapshotId) {
|
||||||
Snapshot snapshot = _snapshotDao.findById(snapshotId);
|
SnapshotVO snapshot = _snapshotDao.findById(snapshotId);
|
||||||
if (snapshot == null) {
|
if (snapshot == null) {
|
||||||
throw new InvalidParameterValueException("No such snapshot");
|
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();
|
Long instanceId = volume.getInstanceId();
|
||||||
|
|
||||||
// If this volume is attached to an VM, then the VM needs to be in the stopped state
|
// 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);
|
SnapshotStrategy snapshotStrategy = _storageStrategyFactory.getSnapshotStrategy(snapshot, SnapshotOperation.REVERT);
|
||||||
|
|
||||||
if (snapshotStrategy == null) {
|
if (snapshotStrategy == null) {
|
||||||
|
|
@ -271,7 +280,15 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager,
|
||||||
return false;
|
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
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -12675,6 +12675,7 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it
|
||||||
background-position: -167px -677px;
|
background-position: -167px -677px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.revertSnapshot .icon,
|
||||||
.restoreVM .icon,
|
.restoreVM .icon,
|
||||||
.restore .icon,
|
.restore .icon,
|
||||||
.recover .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;
|
background-position: -167px -66px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.revertSnapshot:hover .icon,
|
||||||
.restoreVM:hover .icon,
|
.restoreVM:hover .icon,
|
||||||
.restore:hover .icon {
|
.restore:hover .icon {
|
||||||
background-position: -168px -613px;
|
background-position: -168px -613px;
|
||||||
|
|
|
||||||
|
|
@ -1862,6 +1862,9 @@
|
||||||
volumename: {
|
volumename: {
|
||||||
label: 'label.volume'
|
label: 'label.volume'
|
||||||
},
|
},
|
||||||
|
name: {
|
||||||
|
label: 'label.name'
|
||||||
|
},
|
||||||
intervaltype: {
|
intervaltype: {
|
||||||
label: 'label.interval.type'
|
label: 'label.interval.type'
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue