mirror of https://github.com/apache/cloudstack.git
Implementing snapshot-strategy support for reverting a volume to a snapshot
This commit is contained in:
parent
340bcc60c0
commit
e70dde09c9
|
|
@ -38,6 +38,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"),
|
||||
Reverting("Replace the existing volume on a storage system with a snapshot of it"),
|
||||
Resizing("The volume is being resized"),
|
||||
Expunging("The volume is being expunging"),
|
||||
Expunged("The volume is being expunging"),
|
||||
|
|
@ -85,8 +86,11 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba
|
|||
s_fsm.addTransition(Expunging, Event.OperationSucceeded, Expunged);
|
||||
s_fsm.addTransition(Expunging, Event.OperationFailed, Destroy);
|
||||
s_fsm.addTransition(Ready, Event.SnapshotRequested, Snapshotting);
|
||||
s_fsm.addTransition(Ready, Event.RevertRequested, Reverting);
|
||||
s_fsm.addTransition(Snapshotting, Event.OperationSucceeded, Ready);
|
||||
s_fsm.addTransition(Snapshotting, Event.OperationFailed, Ready);
|
||||
s_fsm.addTransition(Reverting, Event.OperationSucceeded, Ready);
|
||||
s_fsm.addTransition(Reverting, Event.OperationFailed, Ready);
|
||||
s_fsm.addTransition(Ready, Event.MigrationRequested, Migrating);
|
||||
s_fsm.addTransition(Migrating, Event.OperationSucceeded, Ready);
|
||||
s_fsm.addTransition(Migrating, Event.OperationFailed, Ready);
|
||||
|
|
@ -111,6 +115,7 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba
|
|||
UploadRequested,
|
||||
MigrationRequested,
|
||||
SnapshotRequested,
|
||||
RevertRequested,
|
||||
DestroyRequested,
|
||||
ExpungingRequested,
|
||||
ResizeRequested;
|
||||
|
|
|
|||
|
|
@ -33,23 +33,35 @@ import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
|
|||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
|
||||
|
||||
import com.cloud.dc.ClusterVO;
|
||||
import com.cloud.dc.dao.ClusterDao;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.host.HostVO;
|
||||
import com.cloud.host.dao.HostDao;
|
||||
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.Snapshot;
|
||||
import com.cloud.storage.SnapshotVO;
|
||||
import com.cloud.storage.Volume;
|
||||
import com.cloud.storage.Volume.Type;
|
||||
import com.cloud.storage.VolumeVO;
|
||||
import com.cloud.storage.dao.SnapshotDao;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.uservm.UserVm;
|
||||
import com.cloud.utils.db.DB;
|
||||
import com.cloud.utils.db.EntityManager;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.fsm.NoTransitionException;
|
||||
import com.cloud.vm.VirtualMachine.State;
|
||||
|
||||
@Component
|
||||
public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
|
||||
private static final Logger s_logger = Logger.getLogger(StorageSystemSnapshotStrategy.class);
|
||||
|
||||
@Inject private ClusterDao _clusterDao;
|
||||
@Inject private DataStoreManager _dataStoreMgr;
|
||||
@Inject private EntityManager _entityMgr;
|
||||
@Inject private HostDao _hostDao;
|
||||
@Inject private SnapshotDao _snapshotDao;
|
||||
@Inject private SnapshotDataFactory _snapshotDataFactory;
|
||||
@Inject private SnapshotDataStoreDao _snapshotStoreDao;
|
||||
|
|
@ -121,6 +133,75 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
|
|||
|
||||
@Override
|
||||
public boolean revertSnapshot(Long snapshotId) {
|
||||
// verify the following:
|
||||
// if root disk, the VM cannot be running (don't allow this at all for ESX)
|
||||
// if data disk, the disk cannot be in the attached state
|
||||
|
||||
SnapshotInfo snapshotInfo = _snapshotDataFactory.getSnapshot(snapshotId, DataStoreRole.Primary);
|
||||
VolumeInfo volumeInfo = snapshotInfo.getBaseVolume();
|
||||
VolumeVO volume = _volumeDao.findById(volumeInfo.getId());
|
||||
|
||||
if (volume.getVolumeType() == Type.DATADISK) {
|
||||
if (volume.getAttached() != null) {
|
||||
throw new CloudRuntimeException("A data disk must be in the detached state in order to perform a revert.");
|
||||
}
|
||||
}
|
||||
else if (volume.getVolumeType() == Type.ROOT) {
|
||||
Long instanceId = volume.getInstanceId();
|
||||
UserVm vm = _entityMgr.findById(UserVm.class, instanceId);
|
||||
|
||||
Long hostId = vm.getHostId();
|
||||
HostVO hostVO = _hostDao.findById(hostId);
|
||||
Long clusterId = hostVO.getClusterId();
|
||||
ClusterVO clusterVO = _clusterDao.findById(clusterId);
|
||||
|
||||
if (clusterVO.getHypervisorType() != HypervisorType.XenServer && clusterVO.getHypervisorType() != HypervisorType.KVM) {
|
||||
throw new CloudRuntimeException("Unsupported hypervisor type for root disk revert. Create a template from this disk and use it instead.");
|
||||
}
|
||||
|
||||
if (vm.getState() != State.Stopped) {
|
||||
throw new CloudRuntimeException("A root disk cannot be reverted unless the VM it's attached to is in the stopped state.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new CloudRuntimeException("Unsupported volume type");
|
||||
}
|
||||
|
||||
SnapshotVO snapshotVO = _snapshotDao.acquireInLockTable(snapshotId);
|
||||
|
||||
if (snapshotVO == null) {
|
||||
throw new CloudRuntimeException("Failed to acquire lock on the following snapshot: " + snapshotId);
|
||||
}
|
||||
|
||||
try {
|
||||
volumeInfo.stateTransit(Volume.Event.RevertRequested);
|
||||
|
||||
boolean result = false;
|
||||
|
||||
try {
|
||||
result = snapshotSvr.revertSnapshot(snapshotId);
|
||||
}
|
||||
finally {
|
||||
if (result) {
|
||||
volumeInfo.stateTransit(Volume.Event.OperationSucceeded);
|
||||
}
|
||||
else {
|
||||
String msg = "Failed to revert the volume to a snapshot";
|
||||
|
||||
s_logger.debug(msg);
|
||||
|
||||
volumeInfo.stateTransit(Volume.Event.OperationFailed);
|
||||
|
||||
throw new CloudRuntimeException("Failed to revert the volume to a snapshot");
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (snapshotVO != null) {
|
||||
_snapshotDao.releaseFromLockTable(snapshotId);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -189,9 +270,9 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
|
|||
@Override
|
||||
public StrategyPriority canHandle(Snapshot snapshot, SnapshotOperation op) {
|
||||
long volumeId = snapshot.getVolumeId();
|
||||
VolumeVO volume = _volumeDao.findById(volumeId);
|
||||
VolumeVO volumeVO = _volumeDao.findById(volumeId);
|
||||
|
||||
long storagePoolId = volume.getPoolId();
|
||||
long storagePoolId = volumeVO.getPoolId();
|
||||
DataStore dataStore = _dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary);
|
||||
|
||||
Map<String, String> mapCapabilities = dataStore.getDriver().getCapabilities();
|
||||
|
|
|
|||
|
|
@ -609,8 +609,31 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CommandResult> callback) {
|
||||
throw new UnsupportedOperationException();
|
||||
public void revertSnapshot(SnapshotInfo snapshotInfo, AsyncCompletionCallback<CommandResult> callback) {
|
||||
String errMsg = null;
|
||||
|
||||
try {
|
||||
VolumeInfo volumeInfo = snapshotInfo.getBaseVolume();
|
||||
|
||||
long storagePoolId = volumeInfo.getPoolId();
|
||||
long sfVolumeId = Long.parseLong(volumeInfo.getFolder());
|
||||
long sfSnapshotId = getSolidFireSnapshotId(snapshotInfo.getId());
|
||||
|
||||
SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao);
|
||||
|
||||
SolidFireUtil.rollBackVolumeToSnapshot(sfConnection, sfVolumeId, sfSnapshotId);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
s_logger.debug(SolidFireUtil.LOG_PREFIX + "Failed to take CloudStack snapshot: " + snapshotInfo.getId(), ex);
|
||||
|
||||
errMsg = ex.getMessage();
|
||||
}
|
||||
|
||||
CommandResult result = new CommandResult();
|
||||
|
||||
result.setResult(errMsg);
|
||||
|
||||
callback.complete(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -689,6 +689,20 @@ public class SolidFireUtil {
|
|||
executeJsonRpc(sfConnection, strSnapshotToDeleteJson);
|
||||
}
|
||||
|
||||
public static void rollBackVolumeToSnapshot(SolidFireConnection sfConnection, long volumeId, long snapshotId) {
|
||||
final Gson gson = new GsonBuilder().create();
|
||||
|
||||
RollbackToInitiate rollbackToInitiate = new RollbackToInitiate(volumeId, snapshotId);
|
||||
|
||||
String strRollbackToInitiateJson = gson.toJson(rollbackToInitiate);
|
||||
|
||||
String strRollbackInitiatedResultJson = executeJsonRpc(sfConnection, strRollbackToInitiateJson);
|
||||
|
||||
RollbackInitiatedResult rollbackInitiatedResult = gson.fromJson(strRollbackInitiatedResultJson, RollbackInitiatedResult.class);
|
||||
|
||||
verifyResult(rollbackInitiatedResult.result, strRollbackInitiatedResultJson, gson);
|
||||
}
|
||||
|
||||
public static long createSolidFireClone(SolidFireConnection sfConnection, long lVolumeId, String cloneName) {
|
||||
final Gson gson = new GsonBuilder().create();
|
||||
|
||||
|
|
@ -1293,6 +1307,26 @@ public class SolidFireUtil {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final class RollbackToInitiate {
|
||||
private final String method = "RollbackToSnapshot";
|
||||
private final RollbackToInitiateParams params;
|
||||
|
||||
private RollbackToInitiate(final long lVolumeId, final long lSnapshotId) {
|
||||
params = new RollbackToInitiateParams(lVolumeId, lSnapshotId);
|
||||
}
|
||||
|
||||
private static final class RollbackToInitiateParams {
|
||||
private long volumeID;
|
||||
private long snapshotID;
|
||||
|
||||
private RollbackToInitiateParams(final long lVolumeId, final long lSnapshotId) {
|
||||
volumeID = lVolumeId;
|
||||
snapshotID = lSnapshotId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final class CloneToCreate {
|
||||
private final String method = "CloneVolume";
|
||||
|
|
@ -1539,6 +1573,15 @@ public class SolidFireUtil {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final class RollbackInitiatedResult {
|
||||
private Result result;
|
||||
|
||||
private static final class Result {
|
||||
private long snapshotID;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class CloneCreateResult {
|
||||
private Result result;
|
||||
|
||||
|
|
|
|||
|
|
@ -138,6 +138,9 @@ import org.apache.cloudstack.api.response.VpnUsersResponse;
|
|||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.cloudstack.config.Configuration;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
|
||||
import org.apache.cloudstack.framework.jobs.AsyncJob;
|
||||
|
|
@ -278,6 +281,7 @@ import com.cloud.storage.UploadVO;
|
|||
import com.cloud.storage.VMTemplateVO;
|
||||
import com.cloud.storage.Volume;
|
||||
import com.cloud.storage.VolumeVO;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.storage.snapshot.SnapshotPolicy;
|
||||
import com.cloud.storage.snapshot.SnapshotSchedule;
|
||||
import com.cloud.template.VirtualMachineTemplate;
|
||||
|
|
@ -307,13 +311,13 @@ public class ApiResponseHelper implements ResponseGenerator {
|
|||
|
||||
private static final Logger s_logger = Logger.getLogger(ApiResponseHelper.class);
|
||||
private static final DecimalFormat s_percentFormat = new DecimalFormat("##.##");
|
||||
|
||||
@Inject
|
||||
private EntityManager _entityMgr;
|
||||
@Inject
|
||||
private UsageService _usageSvc;
|
||||
@Inject
|
||||
NetworkModel _ntwkModel;
|
||||
|
||||
@Inject
|
||||
protected AccountManager _accountMgr;
|
||||
@Inject
|
||||
|
|
@ -322,6 +326,10 @@ public class ApiResponseHelper implements ResponseGenerator {
|
|||
ConfigurationManager _configMgr;
|
||||
@Inject
|
||||
SnapshotDataFactory snapshotfactory;
|
||||
@Inject
|
||||
private VolumeDao _volumeDao;
|
||||
@Inject
|
||||
private DataStoreManager _dataStoreMgr;
|
||||
|
||||
@Override
|
||||
public UserResponse createUserResponse(User user) {
|
||||
|
|
@ -459,10 +467,13 @@ public class ApiResponseHelper implements ResponseGenerator {
|
|||
snapshotResponse.setState(snapshot.getState());
|
||||
|
||||
SnapshotInfo snapshotInfo = null;
|
||||
if (!(snapshot instanceof SnapshotInfo)) {
|
||||
snapshotInfo = snapshotfactory.getSnapshot(snapshot.getId(), DataStoreRole.Image);
|
||||
} else {
|
||||
|
||||
if (snapshot instanceof SnapshotInfo) {
|
||||
snapshotInfo = (SnapshotInfo)snapshot;
|
||||
} else {
|
||||
DataStoreRole dataStoreRole = getDataStoreRole(snapshot);
|
||||
|
||||
snapshotInfo = snapshotfactory.getSnapshot(snapshot.getId(), dataStoreRole);
|
||||
}
|
||||
|
||||
if (snapshotInfo == null) {
|
||||
|
|
@ -485,6 +496,25 @@ public class ApiResponseHelper implements ResponseGenerator {
|
|||
return snapshotResponse;
|
||||
}
|
||||
|
||||
private DataStoreRole getDataStoreRole(Snapshot snapshot) {
|
||||
long volumeId = snapshot.getVolumeId();
|
||||
VolumeVO volumeVO = _volumeDao.findById(volumeId);
|
||||
|
||||
long storagePoolId = volumeVO.getPoolId();
|
||||
DataStore dataStore = _dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary);
|
||||
|
||||
Map<String, String> mapCapabilities = dataStore.getDriver().getCapabilities();
|
||||
|
||||
String value = mapCapabilities.get(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString());
|
||||
Boolean supportsStorageSystemSnapshots = new Boolean(value);
|
||||
|
||||
if (supportsStorageSystemSnapshots) {
|
||||
return DataStoreRole.Primary;
|
||||
}
|
||||
|
||||
return DataStoreRole.Image;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VMSnapshotResponse createVMSnapshotResponse(VMSnapshot vmSnapshot) {
|
||||
VMSnapshotResponse vmSnapshotResponse = new VMSnapshotResponse();
|
||||
|
|
|
|||
Loading…
Reference in New Issue