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 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;

View File

@ -125,4 +125,5 @@ public interface VolumeOrchestrationService {
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.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<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) {

View File

@ -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;
}

View File

@ -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

View File

@ -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,75 +130,207 @@ 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();
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<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 {
_volService.grantAccess(snapshotInfo, hostVO, srcDataStore);
Map<String, String> 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<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());

View File

@ -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();

View File

@ -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();

View File

@ -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");

View File

@ -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();

View File

@ -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);

View File

@ -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) {

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.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 {

View File

@ -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`;