mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Adding support for creating a volume from a snapshot when the snapshot is on managed storage
This commit is contained in:
parent
240e8ef8ac
commit
0f84e042b9
@ -29,8 +29,9 @@ public final class CopyCommand extends Command implements StorageSubSystemComman
|
||||
private DataTO srcTO;
|
||||
private DataTO destTO;
|
||||
private DataTO cacheTO;
|
||||
boolean executeInSequence = false;
|
||||
Map<String, String> options = new HashMap<String, String>();
|
||||
private boolean executeInSequence = false;
|
||||
private Map<String, String> options = new HashMap<String, String>();
|
||||
private Map<String, String> options2 = new HashMap<String, String>();
|
||||
|
||||
public CopyCommand(DataTO srcData, DataTO destData, int timeout, boolean executeInSequence) {
|
||||
super();
|
||||
@ -81,6 +82,14 @@ public final class CopyCommand extends Command implements StorageSubSystemComman
|
||||
return options;
|
||||
}
|
||||
|
||||
public void setOptions2(Map<String, String> options2) {
|
||||
this.options2 = options2;
|
||||
}
|
||||
|
||||
public Map<String, String> getOptions2() {
|
||||
return options2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExecuteInSequence(boolean inSeq) {
|
||||
this.executeInSequence = inSeq;
|
||||
|
||||
@ -125,4 +125,5 @@ public interface VolumeOrchestrationService {
|
||||
|
||||
void updateVolumeDiskChain(long volumeId, String path, String chainInfo);
|
||||
|
||||
VolumeInfo updateHypervisorSnapshotReserveForVolume(DiskOffering diskOffering, VolumeInfo volumeInfo, HypervisorType hyperType);
|
||||
}
|
||||
|
||||
@ -36,6 +36,7 @@ import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationSer
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
|
||||
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.DataStoreDriver;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
|
||||
@ -59,6 +60,7 @@ import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
|
||||
import org.apache.cloudstack.storage.command.CommandResult;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
|
||||
@ -374,16 +376,23 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
||||
|
||||
VolumeInfo vol = volFactory.getVolume(volume.getId());
|
||||
DataStore store = dataStoreMgr.getDataStore(pool.getId(), DataStoreRole.Primary);
|
||||
SnapshotInfo snapInfo = snapshotFactory.getSnapshot(snapshot.getId(), DataStoreRole.Image);
|
||||
DataStoreRole dataStoreRole = getDataStoreRole(snapshot);
|
||||
SnapshotInfo snapInfo = snapshotFactory.getSnapshot(snapshot.getId(), dataStoreRole);
|
||||
|
||||
// don't try to perform a sync if the DataStoreRole of the snapshot is equal to DataStoreRole.Primary
|
||||
if (!DataStoreRole.Primary.equals(dataStoreRole)) {
|
||||
try {
|
||||
// sync snapshot to region store if necessary
|
||||
DataStore snapStore = snapInfo.getDataStore();
|
||||
long snapVolId = snapInfo.getVolumeId();
|
||||
try {
|
||||
|
||||
_snapshotSrv.syncVolumeSnapshotsToRegionStore(snapVolId, snapStore);
|
||||
} catch (Exception ex) {
|
||||
// log but ignore the sync error to avoid any potential S3 down issue, it should be sync next time
|
||||
s_logger.warn(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
// create volume on primary from snapshot
|
||||
AsyncCallFuture<VolumeApiResult> future = volService.createVolumeFromSnapshot(vol, store, snapInfo);
|
||||
try {
|
||||
@ -403,6 +412,30 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
||||
|
||||
}
|
||||
|
||||
public DataStoreRole getDataStoreRole(Snapshot snapshot) {
|
||||
SnapshotDataStoreVO snapshotStore = _snapshotDataStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Primary);
|
||||
|
||||
if (snapshotStore == null) {
|
||||
return DataStoreRole.Image;
|
||||
}
|
||||
|
||||
long storagePoolId = snapshotStore.getDataStoreId();
|
||||
DataStore dataStore = dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary);
|
||||
|
||||
Map<String, String> mapCapabilities = dataStore.getDriver().getCapabilities();
|
||||
|
||||
if (mapCapabilities != null) {
|
||||
String value = mapCapabilities.get(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString());
|
||||
Boolean supportsStorageSystemSnapshots = new Boolean(value);
|
||||
|
||||
if (supportsStorageSystemSnapshots) {
|
||||
return DataStoreRole.Primary;
|
||||
}
|
||||
}
|
||||
|
||||
return DataStoreRole.Image;
|
||||
}
|
||||
|
||||
protected DiskProfile createDiskCharacteristics(VolumeInfo volume, VirtualMachineTemplate template, DataCenter dc, DiskOffering diskOffering) {
|
||||
if (volume.getVolumeType() == Type.ROOT && Storage.ImageFormat.ISO != template.getFormat()) {
|
||||
TemplateDataStoreVO ss = _vmTemplateStoreDao.findByTemplateZoneDownloadStatus(template.getId(), dc.getId(), VMTemplateStorageResourceAssoc.Status.DOWNLOADED);
|
||||
@ -517,7 +550,8 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
||||
// The disk offering can collect this information and pass it on to the volume that's about to be created.
|
||||
// Ex. if you want a 10 GB CloudStack volume to reside on managed storage on Xen, this leads to an SR
|
||||
// that is a total size of (10 GB * (hypervisorSnapshotReserveSpace / 100) + 10 GB).
|
||||
private VolumeInfo updateHypervisorSnapshotReserveForVolume(DiskOffering diskOffering, VolumeInfo volumeInfo, HypervisorType hyperType) {
|
||||
@Override
|
||||
public VolumeInfo updateHypervisorSnapshotReserveForVolume(DiskOffering diskOffering, VolumeInfo volumeInfo, HypervisorType hyperType) {
|
||||
Integer hypervisorSnapshotReserve = diskOffering.getHypervisorSnapshotReserve();
|
||||
|
||||
if (hyperType == HypervisorType.KVM) {
|
||||
|
||||
@ -92,12 +92,18 @@ public class SnapshotVO implements Snapshot {
|
||||
@Column(name = "uuid")
|
||||
String uuid;
|
||||
|
||||
@Column(name = "min_iops")
|
||||
Long minIops;
|
||||
|
||||
@Column(name = "max_iops")
|
||||
Long maxIops;
|
||||
|
||||
public SnapshotVO() {
|
||||
uuid = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
public SnapshotVO(long dcId, long accountId, long domainId, Long volumeId, Long diskOfferingId, String name, short snapshotType, String typeDescription, long size,
|
||||
HypervisorType hypervisorType) {
|
||||
Long minIops, Long maxIops, HypervisorType hypervisorType) {
|
||||
dataCenterId = dcId;
|
||||
this.accountId = accountId;
|
||||
this.domainId = domainId;
|
||||
@ -107,6 +113,8 @@ public class SnapshotVO implements Snapshot {
|
||||
this.snapshotType = snapshotType;
|
||||
this.typeDescription = typeDescription;
|
||||
this.size = size;
|
||||
this.minIops = minIops;
|
||||
this.maxIops = maxIops;
|
||||
state = State.Allocated;
|
||||
this.hypervisorType = hypervisorType;
|
||||
version = "2.2";
|
||||
@ -184,6 +192,14 @@ public class SnapshotVO implements Snapshot {
|
||||
return size;
|
||||
}
|
||||
|
||||
public Long getMinIops() {
|
||||
return minIops;
|
||||
}
|
||||
|
||||
public Long getMaxIops() {
|
||||
return maxIops;
|
||||
}
|
||||
|
||||
public String getTypeDescription() {
|
||||
return typeDescription;
|
||||
}
|
||||
|
||||
@ -164,7 +164,7 @@ public class VolumeVO implements Volume {
|
||||
String reservationId;
|
||||
|
||||
@Column(name = "hv_ss_reserve")
|
||||
Integer hypervisorSnapshotReserve;
|
||||
private Integer hypervisorSnapshotReserve;
|
||||
|
||||
// Real Constructor
|
||||
public VolumeVO(Type type, String name, long dcId, long domainId,
|
||||
@ -628,7 +628,6 @@ public class VolumeVO implements Volume {
|
||||
@Override
|
||||
public Integer getHypervisorSnapshotReserve() {
|
||||
return hypervisorSnapshotReserve;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -27,6 +27,8 @@ import javax.inject.Inject;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
|
||||
@ -34,8 +36,13 @@ 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.SnapshotInfo;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
|
||||
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.engine.subsystem.api.storage.VolumeService.VolumeApiResult;
|
||||
import org.apache.cloudstack.framework.async.AsyncCallFuture;
|
||||
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.storage.command.CopyCommand;
|
||||
@ -56,8 +63,15 @@ import com.cloud.org.Grouping.AllocationState;
|
||||
import com.cloud.resource.ResourceState;
|
||||
import com.cloud.server.ManagementService;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.DiskOfferingVO;
|
||||
import com.cloud.storage.SnapshotVO;
|
||||
import com.cloud.storage.VolumeVO;
|
||||
import com.cloud.storage.Storage.ImageFormat;
|
||||
import com.cloud.storage.dao.DiskOfferingDao;
|
||||
import com.cloud.storage.dao.SnapshotDao;
|
||||
import com.cloud.storage.dao.SnapshotDetailsDao;
|
||||
import com.cloud.storage.dao.SnapshotDetailsVO;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.utils.NumbersUtil;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.vm.VirtualMachineManager;
|
||||
@ -68,16 +82,30 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
|
||||
|
||||
@Inject private AgentManager _agentMgr;
|
||||
@Inject private ConfigurationDao _configDao;
|
||||
@Inject private DiskOfferingDao _diskOfferingDao;
|
||||
@Inject private HostDao _hostDao;
|
||||
@Inject private ManagementService _mgr;
|
||||
@Inject private PrimaryDataStoreDao _storagePoolDao;
|
||||
@Inject private SnapshotDao _snapshotDao;
|
||||
@Inject private SnapshotDetailsDao _snapshotDetailsDao;
|
||||
@Inject private VolumeService _volService;
|
||||
@Inject private VolumeDao _volumeDao;
|
||||
@Inject private VolumeDataFactory _volumeDataFactory;
|
||||
@Inject private VolumeOrchestrationService _volumeMgr;
|
||||
@Inject private VolumeService _volumeService;
|
||||
|
||||
@Override
|
||||
public StrategyPriority canHandle(DataObject srcData, DataObject destData) {
|
||||
if (srcData instanceof SnapshotInfo && destData.getDataStore().getRole() == DataStoreRole.Image || destData.getDataStore().getRole() == DataStoreRole.ImageCache) {
|
||||
DataStore dataStore = srcData.getDataStore();
|
||||
if (srcData instanceof SnapshotInfo) {
|
||||
if (canHandle(srcData.getDataStore()) || canHandle(destData.getDataStore())) {
|
||||
return StrategyPriority.HIGHEST;
|
||||
}
|
||||
}
|
||||
|
||||
return StrategyPriority.CANT_HANDLE;
|
||||
}
|
||||
|
||||
private boolean canHandle(DataStore dataStore) {
|
||||
if (dataStore.getRole() == DataStoreRole.Primary) {
|
||||
Map<String, String> mapCapabilities = dataStore.getDriver().getCapabilities();
|
||||
|
||||
if (mapCapabilities != null) {
|
||||
@ -87,12 +115,12 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
|
||||
if (supportsStorageSystemSnapshots) {
|
||||
s_logger.info("Using 'StorageSystemDataMotionStrategy'");
|
||||
|
||||
return StrategyPriority.HIGHEST;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return StrategyPriority.CANT_HANDLE;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -102,21 +130,64 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
|
||||
|
||||
@Override
|
||||
public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) {
|
||||
if (srcData instanceof SnapshotInfo && destData.getDataStore().getRole() == DataStoreRole.Image || destData.getDataStore().getRole() == DataStoreRole.ImageCache) {
|
||||
if (srcData instanceof SnapshotInfo) {
|
||||
SnapshotInfo snapshotInfo = (SnapshotInfo)srcData;
|
||||
HostVO hostVO = getHost(srcData);
|
||||
DataStore srcDataStore = srcData.getDataStore();
|
||||
|
||||
validate(snapshotInfo);
|
||||
|
||||
boolean canHandleSrc = canHandle(srcData.getDataStore());
|
||||
|
||||
if (canHandleSrc && destData instanceof TemplateInfo &&
|
||||
(destData.getDataStore().getRole() == DataStoreRole.Image || destData.getDataStore().getRole() == DataStoreRole.ImageCache)) {
|
||||
return handleCreateTemplateFromSnapshot(snapshotInfo, (TemplateInfo)destData, callback);
|
||||
}
|
||||
|
||||
if (destData instanceof VolumeInfo) {
|
||||
VolumeInfo volumeInfo = (VolumeInfo)destData;
|
||||
|
||||
boolean canHandleDest = canHandle(destData.getDataStore());
|
||||
|
||||
if (canHandleSrc && canHandleDest) {
|
||||
return handleCreateVolumeFromSnapshotBothOnStorageSystem(snapshotInfo, volumeInfo, callback);
|
||||
}
|
||||
|
||||
if (canHandleSrc) {
|
||||
// return handleCreateVolumeFromSnapshotOnlySourceOnStorageSystem();
|
||||
}
|
||||
|
||||
if (canHandleDest) {
|
||||
// return handleCreateVolumeFromSnapshotOnlyDestinationOnStorageSystem();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException("This operation is not supported.");
|
||||
}
|
||||
|
||||
private void validate(SnapshotInfo snapshotInfo) {
|
||||
long volumeId = snapshotInfo.getVolumeId();
|
||||
|
||||
VolumeVO volumeVO = _volumeDao.findByIdIncludingRemoved(volumeId);
|
||||
|
||||
if (volumeVO.getFormat() != ImageFormat.VHD) {
|
||||
throw new CloudRuntimeException("Only the " + ImageFormat.VHD.toString() + " image type is currently supported.");
|
||||
}
|
||||
}
|
||||
|
||||
private Void handleCreateTemplateFromSnapshot(SnapshotInfo snapshotInfo, TemplateInfo templateInfo, AsyncCompletionCallback<CopyCommandResult> callback) {
|
||||
HostVO hostVO = getHost(snapshotInfo.getDataStore().getId());
|
||||
DataStore srcDataStore = snapshotInfo.getDataStore();
|
||||
|
||||
String value = _configDao.getValue(Config.PrimaryStorageDownloadWait.toString());
|
||||
int primaryStorageDownloadWait = NumbersUtil.parseInt(value, Integer.parseInt(Config.PrimaryStorageDownloadWait.getDefaultValue()));
|
||||
CopyCommand copyCommand = new CopyCommand(srcData.getTO(), destData.getTO(), primaryStorageDownloadWait, VirtualMachineManager.ExecuteInSequence.value());
|
||||
CopyCommand copyCommand = new CopyCommand(snapshotInfo.getTO(), templateInfo.getTO(), primaryStorageDownloadWait, VirtualMachineManager.ExecuteInSequence.value());
|
||||
|
||||
CopyCmdAnswer copyCmdAnswer = null;
|
||||
|
||||
try {
|
||||
_volService.grantAccess(snapshotInfo, hostVO, srcDataStore);
|
||||
_volumeService.grantAccess(snapshotInfo, hostVO, srcDataStore);
|
||||
|
||||
Map<String, String> srcDetails = getSourceDetails(_storagePoolDao.findById(srcDataStore.getId()), snapshotInfo);
|
||||
Map<String, String> srcDetails = getSnapshotDetails(_storagePoolDao.findById(srcDataStore.getId()), snapshotInfo);
|
||||
|
||||
copyCommand.setOptions(srcDetails);
|
||||
|
||||
@ -127,7 +198,7 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
_volService.revokeAccess(snapshotInfo, hostVO, srcDataStore);
|
||||
_volumeService.revokeAccess(snapshotInfo, hostVO, srcDataStore);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
s_logger.debug(ex.getMessage(), ex);
|
||||
@ -150,27 +221,116 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
|
||||
result.setResult(errMsg);
|
||||
|
||||
callback.complete(result);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Map<String, String> getSourceDetails(StoragePoolVO storagePoolVO, SnapshotInfo snapshotInfo) {
|
||||
Map<String, String> srcDetails = new HashMap<String, String>();
|
||||
private Void handleCreateVolumeFromSnapshotBothOnStorageSystem(SnapshotInfo snapshotInfo, VolumeInfo volumeInfo, AsyncCompletionCallback<CopyCommandResult> callback) {
|
||||
try {
|
||||
// at this point, the snapshotInfo and volumeInfo should have the same disk offering ID (so either one should be OK to get a DiskOfferingVO instance)
|
||||
DiskOfferingVO diskOffering = _diskOfferingDao.findByIdIncludingRemoved(volumeInfo.getDiskOfferingId());
|
||||
SnapshotVO snapshot = _snapshotDao.findById(snapshotInfo.getId());
|
||||
|
||||
srcDetails.put(DiskTO.STORAGE_HOST, storagePoolVO.getHostAddress());
|
||||
srcDetails.put(DiskTO.STORAGE_PORT, String.valueOf(storagePoolVO.getPort()));
|
||||
// update the volume's hypervisor_ss_reserve from its disk offering (used for managed storage)
|
||||
_volumeMgr.updateHypervisorSnapshotReserveForVolume(diskOffering, volumeInfo, snapshot.getHypervisorType());
|
||||
|
||||
AsyncCallFuture<VolumeApiResult> future = _volumeService.createVolumeAsync(volumeInfo, volumeInfo.getDataStore());
|
||||
|
||||
VolumeApiResult result = future.get();
|
||||
|
||||
if (result.isFailed()) {
|
||||
s_logger.debug("Failed to create a volume: " + result.getResult());
|
||||
|
||||
throw new CloudRuntimeException(result.getResult());
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new CloudRuntimeException(ex.getMessage());
|
||||
}
|
||||
|
||||
volumeInfo = _volumeDataFactory.getVolume(volumeInfo.getId(), volumeInfo.getDataStore());
|
||||
|
||||
volumeInfo.processEvent(Event.MigrationRequested);
|
||||
|
||||
volumeInfo = _volumeDataFactory.getVolume(volumeInfo.getId(), volumeInfo.getDataStore());
|
||||
|
||||
HostVO hostVO = getHost(snapshotInfo.getDataStore().getId());
|
||||
|
||||
String value = _configDao.getValue(Config.PrimaryStorageDownloadWait.toString());
|
||||
int primaryStorageDownloadWait = NumbersUtil.parseInt(value, Integer.parseInt(Config.PrimaryStorageDownloadWait.getDefaultValue()));
|
||||
CopyCommand copyCommand = new CopyCommand(snapshotInfo.getTO(), volumeInfo.getTO(), primaryStorageDownloadWait, VirtualMachineManager.ExecuteInSequence.value());
|
||||
|
||||
CopyCmdAnswer copyCmdAnswer = null;
|
||||
|
||||
try {
|
||||
_volumeService.grantAccess(snapshotInfo, hostVO, snapshotInfo.getDataStore());
|
||||
_volumeService.grantAccess(volumeInfo, hostVO, volumeInfo.getDataStore());
|
||||
|
||||
Map<String, String> srcDetails = getSnapshotDetails(_storagePoolDao.findById(snapshotInfo.getDataStore().getId()), snapshotInfo);
|
||||
|
||||
copyCommand.setOptions(srcDetails);
|
||||
|
||||
Map<String, String> destDetails = getVolumeDetails(volumeInfo);
|
||||
|
||||
copyCommand.setOptions2(destDetails);
|
||||
|
||||
copyCmdAnswer = (CopyCmdAnswer)_agentMgr.send(hostVO.getId(), copyCommand);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new CloudRuntimeException(ex.getMessage());
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
_volumeService.revokeAccess(snapshotInfo, hostVO, snapshotInfo.getDataStore());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
s_logger.debug(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
try {
|
||||
_volumeService.revokeAccess(volumeInfo, hostVO, volumeInfo.getDataStore());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
s_logger.debug(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
String errMsg = null;
|
||||
|
||||
if (copyCmdAnswer == null || !copyCmdAnswer.getResult()) {
|
||||
if (copyCmdAnswer != null && copyCmdAnswer.getDetails() != null && !copyCmdAnswer.getDetails().isEmpty()) {
|
||||
errMsg = copyCmdAnswer.getDetails();
|
||||
}
|
||||
else {
|
||||
errMsg = "Unable to perform host-side operation";
|
||||
}
|
||||
}
|
||||
|
||||
CopyCommandResult result = new CopyCommandResult(null, copyCmdAnswer);
|
||||
|
||||
result.setResult(errMsg);
|
||||
|
||||
callback.complete(result);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Map<String, String> getSnapshotDetails(StoragePoolVO storagePoolVO, SnapshotInfo snapshotInfo) {
|
||||
Map<String, String> details = new HashMap<String, String>();
|
||||
|
||||
details.put(DiskTO.STORAGE_HOST, storagePoolVO.getHostAddress());
|
||||
details.put(DiskTO.STORAGE_PORT, String.valueOf(storagePoolVO.getPort()));
|
||||
|
||||
long snapshotId = snapshotInfo.getId();
|
||||
|
||||
srcDetails.put(DiskTO.IQN, getProperty(snapshotId, DiskTO.IQN));
|
||||
details.put(DiskTO.IQN, getProperty(snapshotId, DiskTO.IQN));
|
||||
|
||||
srcDetails.put(DiskTO.CHAP_INITIATOR_USERNAME, getProperty(snapshotId, DiskTO.CHAP_INITIATOR_USERNAME));
|
||||
srcDetails.put(DiskTO.CHAP_INITIATOR_SECRET, getProperty(snapshotId, DiskTO.CHAP_INITIATOR_SECRET));
|
||||
srcDetails.put(DiskTO.CHAP_TARGET_USERNAME, getProperty(snapshotId, DiskTO.CHAP_TARGET_USERNAME));
|
||||
srcDetails.put(DiskTO.CHAP_TARGET_SECRET, getProperty(snapshotId, DiskTO.CHAP_TARGET_SECRET));
|
||||
details.put(DiskTO.CHAP_INITIATOR_USERNAME, getProperty(snapshotId, DiskTO.CHAP_INITIATOR_USERNAME));
|
||||
details.put(DiskTO.CHAP_INITIATOR_SECRET, getProperty(snapshotId, DiskTO.CHAP_INITIATOR_SECRET));
|
||||
details.put(DiskTO.CHAP_TARGET_USERNAME, getProperty(snapshotId, DiskTO.CHAP_TARGET_USERNAME));
|
||||
details.put(DiskTO.CHAP_TARGET_SECRET, getProperty(snapshotId, DiskTO.CHAP_TARGET_SECRET));
|
||||
|
||||
return srcDetails;
|
||||
return details;
|
||||
}
|
||||
|
||||
private String getProperty(long snapshotId, String property) {
|
||||
@ -183,8 +343,31 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
|
||||
return null;
|
||||
}
|
||||
|
||||
public HostVO getHost(DataObject srcData) {
|
||||
long dataStoreId = srcData.getDataStore().getId();
|
||||
private Map<String, String> getVolumeDetails(VolumeInfo volumeInfo) {
|
||||
Map<String, String> sourceDetails = new HashMap<String, String>();
|
||||
|
||||
VolumeVO volumeVO = _volumeDao.findById(volumeInfo.getId());
|
||||
|
||||
long storagePoolId = volumeVO.getPoolId();
|
||||
StoragePoolVO storagePoolVO = _storagePoolDao.findById(storagePoolId);
|
||||
|
||||
sourceDetails.put(DiskTO.STORAGE_HOST, storagePoolVO.getHostAddress());
|
||||
sourceDetails.put(DiskTO.STORAGE_PORT, String.valueOf(storagePoolVO.getPort()));
|
||||
sourceDetails.put(DiskTO.IQN, volumeVO.get_iScsiName());
|
||||
|
||||
ChapInfo chapInfo = _volumeService.getChapInfo(volumeInfo, volumeInfo.getDataStore());
|
||||
|
||||
if (chapInfo != null) {
|
||||
sourceDetails.put(DiskTO.CHAP_INITIATOR_USERNAME, chapInfo.getInitiatorUsername());
|
||||
sourceDetails.put(DiskTO.CHAP_INITIATOR_SECRET, chapInfo.getInitiatorSecret());
|
||||
sourceDetails.put(DiskTO.CHAP_TARGET_USERNAME, chapInfo.getTargetUsername());
|
||||
sourceDetails.put(DiskTO.CHAP_TARGET_SECRET, chapInfo.getTargetSecret());
|
||||
}
|
||||
|
||||
return sourceDetails;
|
||||
}
|
||||
|
||||
public HostVO getHost(long dataStoreId) {
|
||||
StoragePoolVO storagePoolVO = _storagePoolDao.findById(dataStoreId);
|
||||
|
||||
List<? extends Cluster> clusters = _mgr.searchForClusters(storagePoolVO.getDataCenterId(), new Long(0), Long.MAX_VALUE, HypervisorType.XenServer.toString());
|
||||
|
||||
@ -154,10 +154,6 @@ public class VolumeObject implements VolumeInfo {
|
||||
return volumeVO.getMaxIops();
|
||||
}
|
||||
|
||||
public void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve) {
|
||||
volumeVO.setHypervisorSnapshotReserve(hypervisorSnapshotReserve);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getHypervisorSnapshotReserve() {
|
||||
return volumeVO.getHypervisorSnapshotReserve();
|
||||
|
||||
@ -1589,6 +1589,10 @@ public class XenServerStorageProcessor implements StorageProcessor {
|
||||
DataTO destData = cmd.getDestTO();
|
||||
DataStoreTO imageStore = srcData.getDataStore();
|
||||
|
||||
if (srcData.getDataStore() instanceof PrimaryDataStoreTO && destData.getDataStore() instanceof PrimaryDataStoreTO) {
|
||||
return createVolumeFromSnapshot2(cmd);
|
||||
}
|
||||
|
||||
if (!(imageStore instanceof NfsTO)) {
|
||||
return new CopyCmdAnswer("unsupported protocol");
|
||||
}
|
||||
@ -1647,6 +1651,51 @@ public class XenServerStorageProcessor implements StorageProcessor {
|
||||
return new CopyCmdAnswer(details);
|
||||
}
|
||||
|
||||
protected Answer createVolumeFromSnapshot2(CopyCommand cmd) {
|
||||
try {
|
||||
Connection conn = hypervisorResource.getConnection();
|
||||
|
||||
Map<String, String> srcOptions = cmd.getOptions();
|
||||
|
||||
String src_iScsiName = srcOptions.get(DiskTO.IQN);
|
||||
String srcStorageHost = srcOptions.get(DiskTO.STORAGE_HOST);
|
||||
String srcChapInitiatorUsername = srcOptions.get(DiskTO.CHAP_INITIATOR_USERNAME);
|
||||
String srcChapInitiatorSecret = srcOptions.get(DiskTO.CHAP_INITIATOR_SECRET);
|
||||
|
||||
SR srcSr = hypervisorResource.getIscsiSR(conn, src_iScsiName, srcStorageHost, src_iScsiName, srcChapInitiatorUsername, srcChapInitiatorSecret, false);
|
||||
|
||||
Map<String, String> destOptions = cmd.getOptions2();
|
||||
|
||||
String dest_iScsiName = destOptions.get(DiskTO.IQN);
|
||||
String destStorageHost = destOptions.get(DiskTO.STORAGE_HOST);
|
||||
String destChapInitiatorUsername = destOptions.get(DiskTO.CHAP_INITIATOR_USERNAME);
|
||||
String destChapInitiatorSecret = destOptions.get(DiskTO.CHAP_INITIATOR_SECRET);
|
||||
|
||||
SR destSr = hypervisorResource.getIscsiSR(conn, dest_iScsiName, destStorageHost, dest_iScsiName, destChapInitiatorUsername, destChapInitiatorSecret, false);
|
||||
|
||||
// there should only be one VDI in this SR
|
||||
VDI srcVdi = srcSr.getVDIs(conn).iterator().next();
|
||||
|
||||
VDI vdiCopy = srcVdi.copy(conn, destSr);
|
||||
|
||||
VolumeObjectTO newVol = new VolumeObjectTO();
|
||||
|
||||
newVol.setSize(vdiCopy.getVirtualSize(conn));
|
||||
newVol.setPath(vdiCopy.getUuid(conn));
|
||||
newVol.setFormat(ImageFormat.VHD);
|
||||
|
||||
hypervisorResource.removeSR(conn, srcSr);
|
||||
hypervisorResource.removeSR(conn, destSr);
|
||||
|
||||
return new CopyCmdAnswer(newVol);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
s_logger.warn("Failed to copy snapshot to volume: " + ex.toString(), ex);
|
||||
|
||||
return new CopyCmdAnswer(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Answer deleteSnapshot(DeleteCommand cmd) {
|
||||
SnapshotObjectTO snapshot = (SnapshotObjectTO) cmd.getData();
|
||||
|
||||
@ -658,6 +658,10 @@ public class Xenserver625StorageProcessor extends XenServerStorageProcessor {
|
||||
VolumeObjectTO volume = (VolumeObjectTO)destData;
|
||||
DataStoreTO imageStore = srcData.getDataStore();
|
||||
|
||||
if (srcData.getDataStore() instanceof PrimaryDataStoreTO && destData.getDataStore() instanceof PrimaryDataStoreTO) {
|
||||
return createVolumeFromSnapshot2(cmd);
|
||||
}
|
||||
|
||||
if (!(imageStore instanceof NfsTO)) {
|
||||
return new CopyCmdAnswer("unsupported protocol");
|
||||
}
|
||||
|
||||
@ -483,7 +483,7 @@ public class ApiResponseHelper implements ResponseGenerator {
|
||||
if (snapshot instanceof SnapshotInfo) {
|
||||
snapshotInfo = (SnapshotInfo)snapshot;
|
||||
} else {
|
||||
DataStoreRole dataStoreRole = getDataStoreRole(snapshot);
|
||||
DataStoreRole dataStoreRole = getDataStoreRole(snapshot, _snapshotStoreDao, _dataStoreMgr);
|
||||
|
||||
snapshotInfo = snapshotfactory.getSnapshot(snapshot.getId(), dataStoreRole);
|
||||
}
|
||||
@ -509,15 +509,15 @@ public class ApiResponseHelper implements ResponseGenerator {
|
||||
return snapshotResponse;
|
||||
}
|
||||
|
||||
private DataStoreRole getDataStoreRole(Snapshot snapshot) {
|
||||
SnapshotDataStoreVO snapshotStore = _snapshotStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Primary);
|
||||
public static DataStoreRole getDataStoreRole(Snapshot snapshot, SnapshotDataStoreDao snapshotStoreDao, DataStoreManager dataStoreMgr) {
|
||||
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Primary);
|
||||
|
||||
if (snapshotStore == null) {
|
||||
return DataStoreRole.Image;
|
||||
}
|
||||
|
||||
long storagePoolId = snapshotStore.getDataStoreId();
|
||||
DataStore dataStore = _dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary);
|
||||
DataStore dataStore = dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary);
|
||||
|
||||
Map<String, String> mapCapabilities = dataStore.getDriver().getCapabilities();
|
||||
|
||||
|
||||
@ -522,6 +522,9 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
||||
size = snapshotCheck.getSize(); // ; disk offering is used for tags
|
||||
// purposes
|
||||
|
||||
minIops = snapshotCheck.getMinIops();
|
||||
maxIops = snapshotCheck.getMaxIops();
|
||||
|
||||
provisioningType = diskOffering.getProvisioningType();
|
||||
// check snapshot permissions
|
||||
_accountMgr.checkAccess(caller, null, true, snapshotCheck);
|
||||
|
||||
@ -87,6 +87,7 @@ import com.cloud.storage.SnapshotPolicyVO;
|
||||
import com.cloud.storage.SnapshotScheduleVO;
|
||||
import com.cloud.storage.SnapshotVO;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.storage.Storage.ImageFormat;
|
||||
import com.cloud.storage.StorageManager;
|
||||
import com.cloud.storage.StoragePool;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
@ -1132,13 +1133,18 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager,
|
||||
StoragePoolVO storagePool = _storagePoolDao.findById(volume.getDataStore().getId());
|
||||
if (storagePool.getScope() == ScopeType.ZONE) {
|
||||
hypervisorType = storagePool.getHypervisor();
|
||||
|
||||
// at the time being, managed storage only supports XenServer, ESX(i), and KVM (i.e. not Hyper-V), so the VHD file type can be mapped to XenServer
|
||||
if (storagePool.isManaged() && HypervisorType.Any.equals(hypervisorType) && ImageFormat.VHD.equals(volume.getFormat())) {
|
||||
hypervisorType = HypervisorType.XenServer;
|
||||
}
|
||||
} else {
|
||||
hypervisorType = volume.getHypervisorType();
|
||||
}
|
||||
|
||||
SnapshotVO snapshotVO =
|
||||
new SnapshotVO(volume.getDataCenterId(), volume.getAccountId(), volume.getDomainId(), volume.getId(), volume.getDiskOfferingId(), snapshotName,
|
||||
(short)snapshotType.ordinal(), snapshotType.name(), volume.getSize(), hypervisorType);
|
||||
(short)snapshotType.ordinal(), snapshotType.name(), volume.getSize(), volume.getMinIops(), volume.getMaxIops(), hypervisorType);
|
||||
|
||||
SnapshotVO snapshot = _snapshotDao.persist(snapshotVO);
|
||||
if (snapshot == null) {
|
||||
|
||||
@ -54,7 +54,6 @@ import org.apache.cloudstack.api.command.user.template.UpdateTemplatePermissions
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
|
||||
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.EndPoint;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
|
||||
@ -81,7 +80,6 @@ import org.apache.cloudstack.storage.command.CommandResult;
|
||||
import org.apache.cloudstack.storage.command.DettachCommand;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
|
||||
@ -97,6 +95,7 @@ import com.cloud.agent.api.to.DataTO;
|
||||
import com.cloud.agent.api.to.DiskTO;
|
||||
import com.cloud.agent.api.to.NfsTO;
|
||||
import com.cloud.api.ApiDBUtils;
|
||||
import com.cloud.api.ApiResponseHelper;
|
||||
import com.cloud.api.query.dao.UserVmJoinDao;
|
||||
import com.cloud.api.query.vo.UserVmJoinVO;
|
||||
import com.cloud.configuration.Config;
|
||||
@ -1379,7 +1378,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
|
||||
AsyncCallFuture<TemplateApiResult> future = null;
|
||||
|
||||
if (snapshotId != null) {
|
||||
DataStoreRole dataStoreRole = getDataStoreRole(snapshot);
|
||||
DataStoreRole dataStoreRole = ApiResponseHelper.getDataStoreRole(snapshot, _snapshotStoreDao, _dataStoreMgr);
|
||||
|
||||
SnapshotInfo snapInfo = _snapshotFactory.getSnapshot(snapshotId, dataStoreRole);
|
||||
|
||||
@ -1474,30 +1473,6 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
|
||||
}
|
||||
}
|
||||
|
||||
private DataStoreRole getDataStoreRole(Snapshot snapshot) {
|
||||
SnapshotDataStoreVO snapshotStore = _snapshotStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Primary);
|
||||
|
||||
if (snapshotStore == null) {
|
||||
return DataStoreRole.Image;
|
||||
}
|
||||
|
||||
long storagePoolId = snapshotStore.getDataStoreId();
|
||||
DataStore dataStore = _dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary);
|
||||
|
||||
Map<String, String> mapCapabilities = dataStore.getDriver().getCapabilities();
|
||||
|
||||
if (mapCapabilities != null) {
|
||||
String value = mapCapabilities.get(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString());
|
||||
Boolean supportsStorageSystemSnapshots = new Boolean(value);
|
||||
|
||||
if (supportsStorageSystemSnapshots) {
|
||||
return DataStoreRole.Primary;
|
||||
}
|
||||
}
|
||||
|
||||
return DataStoreRole.Image;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_TEMPLATE_CREATE, eventDescription = "creating template", create = true)
|
||||
public VMTemplateVO createPrivateTemplateRecord(CreateTemplateCmd cmd, Account templateOwner) throws ResourceAllocationException {
|
||||
|
||||
@ -19,6 +19,9 @@
|
||||
-- Schema upgrade from 4.5.0 to 4.6.0
|
||||
--
|
||||
|
||||
ALTER TABLE `cloud`.`snapshots` ADD COLUMN `min_iops` bigint(20) unsigned COMMENT 'Minimum IOPS';
|
||||
ALTER TABLE `cloud`.`snapshots` ADD COLUMN `max_iops` bigint(20) unsigned COMMENT 'Maximum IOPS';
|
||||
|
||||
INSERT IGNORE INTO `cloud`.`configuration` VALUES ("Advanced", 'DEFAULT', 'management-server', "stats.output.uri", "", "URI to additionally send StatsCollector statistics to", "", NULL, NULL, 0);
|
||||
|
||||
DROP VIEW IF EXISTS `cloud`.`domain_view`;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user