diff --git a/core/src/org/apache/cloudstack/storage/command/CopyCommand.java b/core/src/org/apache/cloudstack/storage/command/CopyCommand.java index 08b4b518cf4..cfede148300 100644 --- a/core/src/org/apache/cloudstack/storage/command/CopyCommand.java +++ b/core/src/org/apache/cloudstack/storage/command/CopyCommand.java @@ -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 options = new HashMap(); + private boolean executeInSequence = false; + private Map options = new HashMap(); + private Map options2 = new HashMap(); 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 options2) { + this.options2 = options2; + } + + public Map getOptions2() { + return options2; + } + @Override public void setExecuteInSequence(boolean inSeq) { this.executeInSequence = inSeq; diff --git a/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java b/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java index 1f198a2710a..497db3d601a 100644 --- a/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java +++ b/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java @@ -125,4 +125,5 @@ public interface VolumeOrchestrationService { void updateVolumeDiskChain(long volumeId, String path, String chainInfo); + VolumeInfo updateHypervisorSnapshotReserveForVolume(DiskOffering diskOffering, VolumeInfo volumeInfo, HypervisorType hyperType); } diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java index 396a49cd24f..ab90fa7e809 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java @@ -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); - // 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); + 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(); + + _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 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 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) { diff --git a/engine/schema/src/com/cloud/storage/SnapshotVO.java b/engine/schema/src/com/cloud/storage/SnapshotVO.java index dae049f6042..950c5e97291 100644 --- a/engine/schema/src/com/cloud/storage/SnapshotVO.java +++ b/engine/schema/src/com/cloud/storage/SnapshotVO.java @@ -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; } diff --git a/engine/schema/src/com/cloud/storage/VolumeVO.java b/engine/schema/src/com/cloud/storage/VolumeVO.java index e2f717d585f..d8323e998f8 100644 --- a/engine/schema/src/com/cloud/storage/VolumeVO.java +++ b/engine/schema/src/com/cloud/storage/VolumeVO.java @@ -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 diff --git a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java index cf9e9dc11a9..525aa709046 100644 --- a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java +++ b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java @@ -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 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,75 +130,207 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy { @Override public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback 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(); - 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()); + validate(snapshotInfo); - CopyCmdAnswer copyCmdAnswer = null; + 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 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(snapshotInfo.getTO(), templateInfo.getTO(), primaryStorageDownloadWait, VirtualMachineManager.ExecuteInSequence.value()); + + CopyCmdAnswer copyCmdAnswer = null; + + try { + _volumeService.grantAccess(snapshotInfo, hostVO, srcDataStore); + + Map srcDetails = getSnapshotDetails(_storagePoolDao.findById(srcDataStore.getId()), snapshotInfo); + + copyCommand.setOptions(srcDetails); + + copyCmdAnswer = (CopyCmdAnswer)_agentMgr.send(hostVO.getId(), copyCommand); + } + catch (Exception ex) { + throw new CloudRuntimeException(ex.getMessage()); + } + finally { try { - _volService.grantAccess(snapshotInfo, hostVO, srcDataStore); - - Map srcDetails = getSourceDetails(_storagePoolDao.findById(srcDataStore.getId()), snapshotInfo); - - copyCommand.setOptions(srcDetails); - - copyCmdAnswer = (CopyCmdAnswer)_agentMgr.send(hostVO.getId(), copyCommand); + _volumeService.revokeAccess(snapshotInfo, hostVO, srcDataStore); } catch (Exception ex) { - throw new CloudRuntimeException(ex.getMessage()); + s_logger.debug(ex.getMessage(), ex); } - finally { - try { - _volService.revokeAccess(snapshotInfo, hostVO, srcDataStore); - } - 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); } + 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 getSourceDetails(StoragePoolVO storagePoolVO, SnapshotInfo snapshotInfo) { - Map srcDetails = new HashMap(); + private Void handleCreateVolumeFromSnapshotBothOnStorageSystem(SnapshotInfo snapshotInfo, VolumeInfo volumeInfo, AsyncCompletionCallback 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 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 srcDetails = getSnapshotDetails(_storagePoolDao.findById(snapshotInfo.getDataStore().getId()), snapshotInfo); + + copyCommand.setOptions(srcDetails); + + Map 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 getSnapshotDetails(StoragePoolVO storagePoolVO, SnapshotInfo snapshotInfo) { + Map details = new HashMap(); + + 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 getVolumeDetails(VolumeInfo volumeInfo) { + Map sourceDetails = new HashMap(); + + 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 clusters = _mgr.searchForClusters(storagePoolVO.getDataCenterId(), new Long(0), Long.MAX_VALUE, HypervisorType.XenServer.toString()); diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java index 6a424353b78..1f574d533ae 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java @@ -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(); diff --git a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessor.java b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessor.java index f261410b1ee..9d483d7ec6f 100644 --- a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessor.java +++ b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessor.java @@ -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 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 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(); diff --git a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/Xenserver625StorageProcessor.java b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/Xenserver625StorageProcessor.java index 0bb02c3a01a..043514bc102 100644 --- a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/Xenserver625StorageProcessor.java +++ b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/Xenserver625StorageProcessor.java @@ -656,7 +656,11 @@ public class Xenserver625StorageProcessor extends XenServerStorageProcessor { DataTO destData = cmd.getDestTO(); PrimaryDataStoreTO pool = (PrimaryDataStoreTO)destData.getDataStore(); VolumeObjectTO volume = (VolumeObjectTO)destData; - DataStoreTO imageStore = srcData.getDataStore(); + 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"); diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 4726c24af2c..98a4a85b15d 100644 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -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 mapCapabilities = dataStore.getDriver().getCapabilities(); diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java index ca838901706..0abb6298534 100644 --- a/server/src/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/com/cloud/storage/VolumeApiServiceImpl.java @@ -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); diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java index 0591175ee24..baa048821c8 100644 --- a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -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) { diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java index f2446735624..330b19edd9c 100644 --- a/server/src/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/com/cloud/template/TemplateManagerImpl.java @@ -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 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 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 { diff --git a/setup/db/db/schema-450to460.sql b/setup/db/db/schema-450to460.sql index 0493184ec11..95d6532c1a2 100644 --- a/setup/db/db/schema-450to460.sql +++ b/setup/db/db/schema-450to460.sql @@ -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`;