Adding support for creating a volume from a snapshot when the snapshot is on managed storage

This commit is contained in:
Mike Tutkowski 2015-01-07 17:21:52 -07:00
parent 240e8ef8ac
commit 0f84e042b9
14 changed files with 387 additions and 109 deletions

View File

@ -29,8 +29,9 @@ public final class CopyCommand extends Command implements StorageSubSystemComman
private DataTO srcTO; private DataTO srcTO;
private DataTO destTO; private DataTO destTO;
private DataTO cacheTO; private DataTO cacheTO;
boolean executeInSequence = false; private boolean executeInSequence = false;
Map<String, String> options = new HashMap<String, String>(); 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) { public CopyCommand(DataTO srcData, DataTO destData, int timeout, boolean executeInSequence) {
super(); super();
@ -81,6 +82,14 @@ public final class CopyCommand extends Command implements StorageSubSystemComman
return options; return options;
} }
public void setOptions2(Map<String, String> options2) {
this.options2 = options2;
}
public Map<String, String> getOptions2() {
return options2;
}
@Override @Override
public void setExecuteInSequence(boolean inSeq) { public void setExecuteInSequence(boolean inSeq) {
this.executeInSequence = inSeq; this.executeInSequence = inSeq;

View File

@ -125,4 +125,5 @@ public interface VolumeOrchestrationService {
void updateVolumeDiskChain(long volumeId, String path, String chainInfo); void updateVolumeDiskChain(long volumeId, String path, String chainInfo);
VolumeInfo updateHypervisorSnapshotReserveForVolume(DiskOffering diskOffering, VolumeInfo volumeInfo, HypervisorType hyperType);
} }

View File

@ -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.ChapInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; 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.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.DataStoreDriver;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore; 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.command.CommandResult;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; 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.StoragePoolVO;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; 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()); VolumeInfo vol = volFactory.getVolume(volume.getId());
DataStore store = dataStoreMgr.getDataStore(pool.getId(), DataStoreRole.Primary); DataStore store = dataStoreMgr.getDataStore(pool.getId(), DataStoreRole.Primary);
SnapshotInfo snapInfo = snapshotFactory.getSnapshot(snapshot.getId(), DataStoreRole.Image); DataStoreRole dataStoreRole = getDataStoreRole(snapshot);
// sync snapshot to region store if necessary SnapshotInfo snapInfo = snapshotFactory.getSnapshot(snapshot.getId(), dataStoreRole);
DataStore snapStore = snapInfo.getDataStore();
long snapVolId = snapInfo.getVolumeId(); // don't try to perform a sync if the DataStoreRole of the snapshot is equal to DataStoreRole.Primary
try { if (!DataStoreRole.Primary.equals(dataStoreRole)) {
_snapshotSrv.syncVolumeSnapshotsToRegionStore(snapVolId, snapStore); try {
} catch (Exception ex) { // sync snapshot to region store if necessary
// log but ignore the sync error to avoid any potential S3 down issue, it should be sync next time DataStore snapStore = snapInfo.getDataStore();
s_logger.warn(ex.getMessage(), ex); 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 // create volume on primary from snapshot
AsyncCallFuture<VolumeApiResult> future = volService.createVolumeFromSnapshot(vol, store, snapInfo); AsyncCallFuture<VolumeApiResult> future = volService.createVolumeFromSnapshot(vol, store, snapInfo);
try { 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) { protected DiskProfile createDiskCharacteristics(VolumeInfo volume, VirtualMachineTemplate template, DataCenter dc, DiskOffering diskOffering) {
if (volume.getVolumeType() == Type.ROOT && Storage.ImageFormat.ISO != template.getFormat()) { if (volume.getVolumeType() == Type.ROOT && Storage.ImageFormat.ISO != template.getFormat()) {
TemplateDataStoreVO ss = _vmTemplateStoreDao.findByTemplateZoneDownloadStatus(template.getId(), dc.getId(), VMTemplateStorageResourceAssoc.Status.DOWNLOADED); 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. // 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 // 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). // 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(); Integer hypervisorSnapshotReserve = diskOffering.getHypervisorSnapshotReserve();
if (hyperType == HypervisorType.KVM) { if (hyperType == HypervisorType.KVM) {

View File

@ -92,12 +92,18 @@ public class SnapshotVO implements Snapshot {
@Column(name = "uuid") @Column(name = "uuid")
String uuid; String uuid;
@Column(name = "min_iops")
Long minIops;
@Column(name = "max_iops")
Long maxIops;
public SnapshotVO() { public SnapshotVO() {
uuid = UUID.randomUUID().toString(); uuid = UUID.randomUUID().toString();
} }
public SnapshotVO(long dcId, long accountId, long domainId, Long volumeId, Long diskOfferingId, String name, short snapshotType, String typeDescription, long size, 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; dataCenterId = dcId;
this.accountId = accountId; this.accountId = accountId;
this.domainId = domainId; this.domainId = domainId;
@ -107,6 +113,8 @@ public class SnapshotVO implements Snapshot {
this.snapshotType = snapshotType; this.snapshotType = snapshotType;
this.typeDescription = typeDescription; this.typeDescription = typeDescription;
this.size = size; this.size = size;
this.minIops = minIops;
this.maxIops = maxIops;
state = State.Allocated; state = State.Allocated;
this.hypervisorType = hypervisorType; this.hypervisorType = hypervisorType;
version = "2.2"; version = "2.2";
@ -184,6 +192,14 @@ public class SnapshotVO implements Snapshot {
return size; return size;
} }
public Long getMinIops() {
return minIops;
}
public Long getMaxIops() {
return maxIops;
}
public String getTypeDescription() { public String getTypeDescription() {
return typeDescription; return typeDescription;
} }

View File

@ -164,7 +164,7 @@ public class VolumeVO implements Volume {
String reservationId; String reservationId;
@Column(name = "hv_ss_reserve") @Column(name = "hv_ss_reserve")
Integer hypervisorSnapshotReserve; private Integer hypervisorSnapshotReserve;
// Real Constructor // Real Constructor
public VolumeVO(Type type, String name, long dcId, long domainId, public VolumeVO(Type type, String name, long dcId, long domainId,
@ -628,7 +628,6 @@ public class VolumeVO implements Volume {
@Override @Override
public Integer getHypervisorSnapshotReserve() { public Integer getHypervisorSnapshotReserve() {
return hypervisorSnapshotReserve; return hypervisorSnapshotReserve;
} }
@Override @Override

View File

@ -27,6 +27,8 @@ import javax.inject.Inject;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.springframework.stereotype.Component; 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.CopyCommandResult;
import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy; import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy;
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; 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.DataStoreCapabilities;
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; 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.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.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.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.async.AsyncCompletionCallback;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.storage.command.CopyCommand; 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.resource.ResourceState;
import com.cloud.server.ManagementService; import com.cloud.server.ManagementService;
import com.cloud.storage.DataStoreRole; 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.SnapshotDetailsDao;
import com.cloud.storage.dao.SnapshotDetailsVO; import com.cloud.storage.dao.SnapshotDetailsVO;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.utils.NumbersUtil; import com.cloud.utils.NumbersUtil;
import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.VirtualMachineManager;
@ -68,16 +82,30 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
@Inject private AgentManager _agentMgr; @Inject private AgentManager _agentMgr;
@Inject private ConfigurationDao _configDao; @Inject private ConfigurationDao _configDao;
@Inject private DiskOfferingDao _diskOfferingDao;
@Inject private HostDao _hostDao; @Inject private HostDao _hostDao;
@Inject private ManagementService _mgr; @Inject private ManagementService _mgr;
@Inject private PrimaryDataStoreDao _storagePoolDao; @Inject private PrimaryDataStoreDao _storagePoolDao;
@Inject private SnapshotDao _snapshotDao;
@Inject private SnapshotDetailsDao _snapshotDetailsDao; @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 @Override
public StrategyPriority canHandle(DataObject srcData, DataObject destData) { public StrategyPriority canHandle(DataObject srcData, DataObject destData) {
if (srcData instanceof SnapshotInfo && destData.getDataStore().getRole() == DataStoreRole.Image || destData.getDataStore().getRole() == DataStoreRole.ImageCache) { if (srcData instanceof SnapshotInfo) {
DataStore dataStore = srcData.getDataStore(); 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(); Map<String, String> mapCapabilities = dataStore.getDriver().getCapabilities();
if (mapCapabilities != null) { if (mapCapabilities != null) {
@ -87,12 +115,12 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
if (supportsStorageSystemSnapshots) { if (supportsStorageSystemSnapshots) {
s_logger.info("Using 'StorageSystemDataMotionStrategy'"); s_logger.info("Using 'StorageSystemDataMotionStrategy'");
return StrategyPriority.HIGHEST; return true;
} }
} }
} }
return StrategyPriority.CANT_HANDLE; return false;
} }
@Override @Override
@ -102,75 +130,207 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
@Override @Override
public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) { 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; SnapshotInfo snapshotInfo = (SnapshotInfo)srcData;
HostVO hostVO = getHost(srcData);
DataStore srcDataStore = srcData.getDataStore();
String value = _configDao.getValue(Config.PrimaryStorageDownloadWait.toString()); validate(snapshotInfo);
int primaryStorageDownloadWait = NumbersUtil.parseInt(value, Integer.parseInt(Config.PrimaryStorageDownloadWait.getDefaultValue()));
CopyCommand copyCommand = new CopyCommand(srcData.getTO(), destData.getTO(), primaryStorageDownloadWait, VirtualMachineManager.ExecuteInSequence.value());
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<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(snapshotInfo.getTO(), templateInfo.getTO(), primaryStorageDownloadWait, VirtualMachineManager.ExecuteInSequence.value());
CopyCmdAnswer copyCmdAnswer = null;
try {
_volumeService.grantAccess(snapshotInfo, hostVO, srcDataStore);
Map<String, String> 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 { try {
_volService.grantAccess(snapshotInfo, hostVO, srcDataStore); _volumeService.revokeAccess(snapshotInfo, hostVO, srcDataStore);
Map<String, String> srcDetails = getSourceDetails(_storagePoolDao.findById(srcDataStore.getId()), snapshotInfo);
copyCommand.setOptions(srcDetails);
copyCmdAnswer = (CopyCmdAnswer)_agentMgr.send(hostVO.getId(), copyCommand);
} }
catch (Exception ex) { 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; return null;
} }
private Map<String, String> getSourceDetails(StoragePoolVO storagePoolVO, SnapshotInfo snapshotInfo) { private Void handleCreateVolumeFromSnapshotBothOnStorageSystem(SnapshotInfo snapshotInfo, VolumeInfo volumeInfo, AsyncCompletionCallback<CopyCommandResult> callback) {
Map<String, String> srcDetails = new HashMap<String, String>(); 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()); // update the volume's hypervisor_ss_reserve from its disk offering (used for managed storage)
srcDetails.put(DiskTO.STORAGE_PORT, String.valueOf(storagePoolVO.getPort())); _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(); 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)); details.put(DiskTO.CHAP_INITIATOR_USERNAME, getProperty(snapshotId, DiskTO.CHAP_INITIATOR_USERNAME));
srcDetails.put(DiskTO.CHAP_INITIATOR_SECRET, getProperty(snapshotId, DiskTO.CHAP_INITIATOR_SECRET)); details.put(DiskTO.CHAP_INITIATOR_SECRET, getProperty(snapshotId, DiskTO.CHAP_INITIATOR_SECRET));
srcDetails.put(DiskTO.CHAP_TARGET_USERNAME, getProperty(snapshotId, DiskTO.CHAP_TARGET_USERNAME)); details.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_TARGET_SECRET, getProperty(snapshotId, DiskTO.CHAP_TARGET_SECRET));
return srcDetails; return details;
} }
private String getProperty(long snapshotId, String property) { private String getProperty(long snapshotId, String property) {
@ -183,8 +343,31 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
return null; return null;
} }
public HostVO getHost(DataObject srcData) { private Map<String, String> getVolumeDetails(VolumeInfo volumeInfo) {
long dataStoreId = srcData.getDataStore().getId(); 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); StoragePoolVO storagePoolVO = _storagePoolDao.findById(dataStoreId);
List<? extends Cluster> clusters = _mgr.searchForClusters(storagePoolVO.getDataCenterId(), new Long(0), Long.MAX_VALUE, HypervisorType.XenServer.toString()); List<? extends Cluster> clusters = _mgr.searchForClusters(storagePoolVO.getDataCenterId(), new Long(0), Long.MAX_VALUE, HypervisorType.XenServer.toString());

View File

@ -154,10 +154,6 @@ public class VolumeObject implements VolumeInfo {
return volumeVO.getMaxIops(); return volumeVO.getMaxIops();
} }
public void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve) {
volumeVO.setHypervisorSnapshotReserve(hypervisorSnapshotReserve);
}
@Override @Override
public Integer getHypervisorSnapshotReserve() { public Integer getHypervisorSnapshotReserve() {
return volumeVO.getHypervisorSnapshotReserve(); return volumeVO.getHypervisorSnapshotReserve();

View File

@ -1589,6 +1589,10 @@ public class XenServerStorageProcessor implements StorageProcessor {
DataTO destData = cmd.getDestTO(); DataTO destData = cmd.getDestTO();
DataStoreTO imageStore = srcData.getDataStore(); DataStoreTO imageStore = srcData.getDataStore();
if (srcData.getDataStore() instanceof PrimaryDataStoreTO && destData.getDataStore() instanceof PrimaryDataStoreTO) {
return createVolumeFromSnapshot2(cmd);
}
if (!(imageStore instanceof NfsTO)) { if (!(imageStore instanceof NfsTO)) {
return new CopyCmdAnswer("unsupported protocol"); return new CopyCmdAnswer("unsupported protocol");
} }
@ -1647,6 +1651,51 @@ public class XenServerStorageProcessor implements StorageProcessor {
return new CopyCmdAnswer(details); 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 @Override
public Answer deleteSnapshot(DeleteCommand cmd) { public Answer deleteSnapshot(DeleteCommand cmd) {
SnapshotObjectTO snapshot = (SnapshotObjectTO) cmd.getData(); SnapshotObjectTO snapshot = (SnapshotObjectTO) cmd.getData();

View File

@ -656,7 +656,11 @@ public class Xenserver625StorageProcessor extends XenServerStorageProcessor {
DataTO destData = cmd.getDestTO(); DataTO destData = cmd.getDestTO();
PrimaryDataStoreTO pool = (PrimaryDataStoreTO)destData.getDataStore(); PrimaryDataStoreTO pool = (PrimaryDataStoreTO)destData.getDataStore();
VolumeObjectTO volume = (VolumeObjectTO)destData; 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)) { if (!(imageStore instanceof NfsTO)) {
return new CopyCmdAnswer("unsupported protocol"); return new CopyCmdAnswer("unsupported protocol");

View File

@ -483,7 +483,7 @@ public class ApiResponseHelper implements ResponseGenerator {
if (snapshot instanceof SnapshotInfo) { if (snapshot instanceof SnapshotInfo) {
snapshotInfo = (SnapshotInfo)snapshot; snapshotInfo = (SnapshotInfo)snapshot;
} else { } else {
DataStoreRole dataStoreRole = getDataStoreRole(snapshot); DataStoreRole dataStoreRole = getDataStoreRole(snapshot, _snapshotStoreDao, _dataStoreMgr);
snapshotInfo = snapshotfactory.getSnapshot(snapshot.getId(), dataStoreRole); snapshotInfo = snapshotfactory.getSnapshot(snapshot.getId(), dataStoreRole);
} }
@ -509,15 +509,15 @@ public class ApiResponseHelper implements ResponseGenerator {
return snapshotResponse; return snapshotResponse;
} }
private DataStoreRole getDataStoreRole(Snapshot snapshot) { public static DataStoreRole getDataStoreRole(Snapshot snapshot, SnapshotDataStoreDao snapshotStoreDao, DataStoreManager dataStoreMgr) {
SnapshotDataStoreVO snapshotStore = _snapshotStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Primary); SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Primary);
if (snapshotStore == null) { if (snapshotStore == null) {
return DataStoreRole.Image; return DataStoreRole.Image;
} }
long storagePoolId = snapshotStore.getDataStoreId(); 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(); Map<String, String> mapCapabilities = dataStore.getDriver().getCapabilities();

View File

@ -522,6 +522,9 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
size = snapshotCheck.getSize(); // ; disk offering is used for tags size = snapshotCheck.getSize(); // ; disk offering is used for tags
// purposes // purposes
minIops = snapshotCheck.getMinIops();
maxIops = snapshotCheck.getMaxIops();
provisioningType = diskOffering.getProvisioningType(); provisioningType = diskOffering.getProvisioningType();
// check snapshot permissions // check snapshot permissions
_accountMgr.checkAccess(caller, null, true, snapshotCheck); _accountMgr.checkAccess(caller, null, true, snapshotCheck);

View File

@ -87,6 +87,7 @@ import com.cloud.storage.SnapshotPolicyVO;
import com.cloud.storage.SnapshotScheduleVO; import com.cloud.storage.SnapshotScheduleVO;
import com.cloud.storage.SnapshotVO; import com.cloud.storage.SnapshotVO;
import com.cloud.storage.Storage; import com.cloud.storage.Storage;
import com.cloud.storage.Storage.ImageFormat;
import com.cloud.storage.StorageManager; import com.cloud.storage.StorageManager;
import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePool;
import com.cloud.storage.VMTemplateVO; import com.cloud.storage.VMTemplateVO;
@ -1132,13 +1133,18 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager,
StoragePoolVO storagePool = _storagePoolDao.findById(volume.getDataStore().getId()); StoragePoolVO storagePool = _storagePoolDao.findById(volume.getDataStore().getId());
if (storagePool.getScope() == ScopeType.ZONE) { if (storagePool.getScope() == ScopeType.ZONE) {
hypervisorType = storagePool.getHypervisor(); 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 { } else {
hypervisorType = volume.getHypervisorType(); hypervisorType = volume.getHypervisorType();
} }
SnapshotVO snapshotVO = SnapshotVO snapshotVO =
new SnapshotVO(volume.getDataCenterId(), volume.getAccountId(), volume.getDomainId(), volume.getId(), volume.getDiskOfferingId(), snapshotName, 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); SnapshotVO snapshot = _snapshotDao.persist(snapshotVO);
if (snapshot == null) { if (snapshot == null) {

View File

@ -54,7 +54,6 @@ import org.apache.cloudstack.api.command.user.template.UpdateTemplatePermissions
import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; 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.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.DataStoreManager;
import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; 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.command.DettachCommand;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; 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.StoragePoolVO;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; 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.DiskTO;
import com.cloud.agent.api.to.NfsTO; import com.cloud.agent.api.to.NfsTO;
import com.cloud.api.ApiDBUtils; import com.cloud.api.ApiDBUtils;
import com.cloud.api.ApiResponseHelper;
import com.cloud.api.query.dao.UserVmJoinDao; import com.cloud.api.query.dao.UserVmJoinDao;
import com.cloud.api.query.vo.UserVmJoinVO; import com.cloud.api.query.vo.UserVmJoinVO;
import com.cloud.configuration.Config; import com.cloud.configuration.Config;
@ -1379,7 +1378,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
AsyncCallFuture<TemplateApiResult> future = null; AsyncCallFuture<TemplateApiResult> future = null;
if (snapshotId != null) { if (snapshotId != null) {
DataStoreRole dataStoreRole = getDataStoreRole(snapshot); DataStoreRole dataStoreRole = ApiResponseHelper.getDataStoreRole(snapshot, _snapshotStoreDao, _dataStoreMgr);
SnapshotInfo snapInfo = _snapshotFactory.getSnapshot(snapshotId, dataStoreRole); 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 @Override
@ActionEvent(eventType = EventTypes.EVENT_TEMPLATE_CREATE, eventDescription = "creating template", create = true) @ActionEvent(eventType = EventTypes.EVENT_TEMPLATE_CREATE, eventDescription = "creating template", create = true)
public VMTemplateVO createPrivateTemplateRecord(CreateTemplateCmd cmd, Account templateOwner) throws ResourceAllocationException { public VMTemplateVO createPrivateTemplateRecord(CreateTemplateCmd cmd, Account templateOwner) throws ResourceAllocationException {

View File

@ -19,6 +19,9 @@
-- Schema upgrade from 4.5.0 to 4.6.0 -- 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); 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`; DROP VIEW IF EXISTS `cloud`.`domain_view`;