You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by mt...@apache.org on 2015/01/20 23:38:32 UTC
git commit: updated refs/heads/master to 0f84e04
Repository: cloudstack
Updated Branches:
refs/heads/master 240e8ef8a -> 0f84e042b
Adding support for creating a volume from a snapshot when the snapshot is on managed storage
Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/0f84e042
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/0f84e042
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/0f84e042
Branch: refs/heads/master
Commit: 0f84e042b99e58e3d2293c0a988e1baa019c8c56
Parents: 240e8ef
Author: Mike Tutkowski <mi...@solidfire.com>
Authored: Wed Jan 7 17:21:52 2015 -0700
Committer: Mike Tutkowski <mi...@solidfire.com>
Committed: Tue Jan 20 15:24:33 2015 -0700
----------------------------------------------------------------------
.../cloudstack/storage/command/CopyCommand.java | 13 +-
.../service/VolumeOrchestrationService.java | 1 +
.../orchestration/VolumeOrchestrator.java | 54 +++-
.../src/com/cloud/storage/SnapshotVO.java | 18 +-
.../schema/src/com/cloud/storage/VolumeVO.java | 3 +-
.../motion/StorageSystemDataMotionStrategy.java | 279 +++++++++++++++----
.../cloudstack/storage/volume/VolumeObject.java | 4 -
.../resource/XenServerStorageProcessor.java | 49 ++++
.../resource/Xenserver625StorageProcessor.java | 6 +-
server/src/com/cloud/api/ApiResponseHelper.java | 8 +-
.../com/cloud/storage/VolumeApiServiceImpl.java | 3 +
.../storage/snapshot/SnapshotManagerImpl.java | 8 +-
.../com/cloud/template/TemplateManagerImpl.java | 29 +-
setup/db/db/schema-450to460.sql | 3 +
14 files changed, 378 insertions(+), 100 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/core/src/org/apache/cloudstack/storage/command/CopyCommand.java
----------------------------------------------------------------------
diff --git a/core/src/org/apache/cloudstack/storage/command/CopyCommand.java b/core/src/org/apache/cloudstack/storage/command/CopyCommand.java
index 08b4b51..cfede14 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<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;
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java
----------------------------------------------------------------------
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 1f198a2..497db3d 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);
}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
----------------------------------------------------------------------
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 396a49c..ab90fa7 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<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) {
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/engine/schema/src/com/cloud/storage/SnapshotVO.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/storage/SnapshotVO.java b/engine/schema/src/com/cloud/storage/SnapshotVO.java
index dae049f..950c5e9 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;
}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/engine/schema/src/com/cloud/storage/VolumeVO.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/storage/VolumeVO.java b/engine/schema/src/com/cloud/storage/VolumeVO.java
index e2f717d..d8323e9 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
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java
----------------------------------------------------------------------
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 cf9e9dc..525aa70 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<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());
- try {
- _volService.grantAccess(snapshotInfo, hostVO, srcDataStore);
+ if (canHandleSrc && destData instanceof TemplateInfo &&
+ (destData.getDataStore().getRole() == DataStoreRole.Image || destData.getDataStore().getRole() == DataStoreRole.ImageCache)) {
+ return handleCreateTemplateFromSnapshot(snapshotInfo, (TemplateInfo)destData, callback);
+ }
- Map<String, String> srcDetails = getSourceDetails(_storagePoolDao.findById(srcDataStore.getId()), snapshotInfo);
+ if (destData instanceof VolumeInfo) {
+ VolumeInfo volumeInfo = (VolumeInfo)destData;
+
+ boolean canHandleDest = canHandle(destData.getDataStore());
+
+ if (canHandleSrc && canHandleDest) {
+ return handleCreateVolumeFromSnapshotBothOnStorageSystem(snapshotInfo, volumeInfo, callback);
+ }
- copyCommand.setOptions(srcDetails);
+ if (canHandleSrc) {
+ // return handleCreateVolumeFromSnapshotOnlySourceOnStorageSystem();
+ }
- copyCmdAnswer = (CopyCmdAnswer)_agentMgr.send(hostVO.getId(), copyCommand);
+ 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 {
+ _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";
}
+ }
- String errMsg = null;
+ CopyCommandResult result = new CopyCommandResult(null, copyCmdAnswer);
- 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";
- }
+ result.setResult(errMsg);
+
+ callback.complete(result);
+
+ return null;
+ }
+
+ 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());
+
+ // 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);
}
- CopyCommandResult result = new CopyCommandResult(null, copyCmdAnswer);
+ try {
+ _volumeService.revokeAccess(volumeInfo, hostVO, volumeInfo.getDataStore());
+ }
+ catch (Exception ex) {
+ s_logger.debug(ex.getMessage(), ex);
+ }
+ }
- result.setResult(errMsg);
+ String errMsg = null;
- callback.complete(result);
+ 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 Map<String, String> getSnapshotDetails(StoragePoolVO storagePoolVO, SnapshotInfo snapshotInfo) {
+ Map<String, String> details = new HashMap<String, String>();
- srcDetails.put(DiskTO.STORAGE_HOST, storagePoolVO.getHostAddress());
- srcDetails.put(DiskTO.STORAGE_PORT, String.valueOf(storagePoolVO.getPort()));
+ 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());
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java
----------------------------------------------------------------------
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 6a42435..1f574d5 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();
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessor.java
----------------------------------------------------------------------
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 f261410..9d483d7 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<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();
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/Xenserver625StorageProcessor.java
----------------------------------------------------------------------
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 0bb02c3..043514b 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");
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/server/src/com/cloud/api/ApiResponseHelper.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java
index 4726c24..98a4a85 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<String, String> mapCapabilities = dataStore.getDriver().getCapabilities();
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/server/src/com/cloud/storage/VolumeApiServiceImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java
index ca83890..0abb629 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);
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java
index 0591175..baa0488 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) {
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/server/src/com/cloud/template/TemplateManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java
index f244673..330b19e 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<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 {
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0f84e042/setup/db/db/schema-450to460.sql
----------------------------------------------------------------------
diff --git a/setup/db/db/schema-450to460.sql b/setup/db/db/schema-450to460.sql
index 0493184..95d6532 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`;