From b06e66c50a43337d28c4157c396e39adb488902f Mon Sep 17 00:00:00 2001 From: Mike Tutkowski Date: Thu, 27 Feb 2014 13:20:56 -0700 Subject: [PATCH] CLOUDSTACK-6170 --- .../offering/CreateServiceOfferingCmd.java | 32 ++++ .../storage/to/PrimaryDataStoreTO.java | 18 ++ .../api/storage/DataMotionService.java | 2 + .../api/storage/DataMotionStrategy.java | 2 + .../api/storage/PrimaryDataStoreInfo.java | 18 ++ .../subsystem/api/storage/VolumeService.java | 6 +- .../orchestration/VolumeOrchestrator.java | 19 +- .../motion/AncientDataMotionStrategy.java | 25 ++- .../storage/motion/DataMotionServiceImpl.java | 9 +- .../storage/image/store/TemplateObject.java | 15 ++ .../test/MockStorageMotionStrategy.java | 5 + .../datastore/PrimaryDataStoreImpl.java | 20 +- .../provider/DefaultHostListener.java | 2 +- .../storage/volume/VolumeServiceImpl.java | 156 +++++++++++++++- .../motion/SimulatorDataMotionStrategy.java | 5 + .../motion/VmwareStorageMotionStrategy.java | 5 + .../xen/resource/CitrixResourceBase.java | 11 +- .../resource/XenServerStorageProcessor.java | 128 +++++++++---- .../XenServerStorageMotionStrategy.java | 5 + .../ConfigurationManagerImpl.java | 49 ++++- ui/scripts/configuration.js | 174 ++++++++++++++++-- 21 files changed, 639 insertions(+), 67 deletions(-) diff --git a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java index 1d8cbff9307..4143c2e44c7 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java @@ -27,6 +27,7 @@ import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.BaseCmd.CommandType; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; @@ -117,6 +118,21 @@ public class CreateServiceOfferingCmd extends BaseCmd { @Parameter(name = ApiConstants.IOPS_WRITE_RATE, type = CommandType.LONG, required = false, description = "io requests write rate of the disk offering") private Long iopsWriteRate; + @Parameter(name = ApiConstants.CUSTOMIZED_IOPS, type = CommandType.BOOLEAN, required = false, description = "whether compute offering iops is custom or not") + private Boolean customizedIops; + + @Parameter(name = ApiConstants.MIN_IOPS, type = CommandType.LONG, required = false, description = "min iops of the compute offering") + private Long minIops; + + @Parameter(name = ApiConstants.MAX_IOPS, type = CommandType.LONG, required = false, description = "max iops of the compute offering") + private Long maxIops; + + @Parameter(name = ApiConstants.HYPERVISOR_SNAPSHOT_RESERVE, + type = CommandType.INTEGER, + required = false, + description = "Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)") + private Integer hypervisorSnapshotReserve; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -215,6 +231,22 @@ public class CreateServiceOfferingCmd extends BaseCmd { return iopsWriteRate; } + public Boolean isCustomizedIops() { + return customizedIops; + } + + public Long getMinIops() { + return minIops; + } + + public Long getMaxIops() { + return maxIops; + } + + public Integer getHypervisorSnapshotReserve() { + return hypervisorSnapshotReserve; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/core/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java b/core/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java index 01c05f004f9..6a5d6793312 100644 --- a/core/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java +++ b/core/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java @@ -16,6 +16,8 @@ // under the License. package org.apache.cloudstack.storage.to; +import java.util.Map; + import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore; import com.cloud.agent.api.to.DataStoreTO; @@ -23,6 +25,16 @@ import com.cloud.storage.DataStoreRole; import com.cloud.storage.Storage.StoragePoolType; public class PrimaryDataStoreTO implements DataStoreTO { + public static final String MANAGED = PrimaryDataStore.MANAGED; + public static final String STORAGE_HOST = PrimaryDataStore.STORAGE_HOST; + public static final String MANAGED_STORE_TARGET = PrimaryDataStore.MANAGED_STORE_TARGET; + public static final String MANAGED_STORE_TARGET_ROOT_VOLUME = PrimaryDataStore.MANAGED_STORE_TARGET_ROOT_VOLUME; + public static final String CHAP_INITIATOR_USERNAME = PrimaryDataStore.CHAP_INITIATOR_USERNAME; + public static final String CHAP_INITIATOR_SECRET = PrimaryDataStore.CHAP_INITIATOR_SECRET; + public static final String CHAP_TARGET_USERNAME = PrimaryDataStore.CHAP_TARGET_USERNAME; + public static final String CHAP_TARGET_SECRET = PrimaryDataStore.CHAP_TARGET_SECRET; + public static final String VOLUME_SIZE = PrimaryDataStore.VOLUME_SIZE; + private final String uuid; private final String name; private String type; @@ -32,6 +44,7 @@ public class PrimaryDataStoreTO implements DataStoreTO { private String path; private int port; private final String url; + private Map details; public PrimaryDataStoreTO(PrimaryDataStore dataStore) { this.uuid = dataStore.getUuid(); @@ -42,6 +55,7 @@ public class PrimaryDataStoreTO implements DataStoreTO { this.setPath(dataStore.getPath()); this.setPort(dataStore.getPort()); this.url = dataStore.getUri(); + this.details = dataStore.getDetails(); } public long getId() { @@ -58,6 +72,10 @@ public class PrimaryDataStoreTO implements DataStoreTO { return this.url; } + public Map getDetails() { + return this.details; + } + public String getName() { return this.name; } diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionService.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionService.java index f20e18847fe..a8f8b308539 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionService.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionService.java @@ -26,6 +26,8 @@ import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.host.Host; public interface DataMotionService { + void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback callback); + void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback); void copyAsync(Map volumeMap, VirtualMachineTO vmTo, Host srcHost, Host destHost, AsyncCompletionCallback callback); diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionStrategy.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionStrategy.java index 39a257d7262..b5601e9693c 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionStrategy.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionStrategy.java @@ -30,6 +30,8 @@ public interface DataMotionStrategy { StrategyPriority canHandle(Map volumeMap, Host srcHost, Host destHost); + Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback callback); + Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback); Void copyAsync(Map volumeMap, VirtualMachineTO vmTo, Host srcHost, Host destHost, AsyncCompletionCallback callback); diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreInfo.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreInfo.java index 19893d07d1f..bb9f2dad1ff 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreInfo.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreInfo.java @@ -18,6 +18,8 @@ */ package org.apache.cloudstack.engine.subsystem.api.storage; +import java.util.Map; + import org.apache.cloudstack.engine.subsystem.api.storage.disktype.DiskFormat; import com.cloud.hypervisor.Hypervisor.HypervisorType; @@ -25,6 +27,16 @@ import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StoragePool; public interface PrimaryDataStoreInfo extends StoragePool { + static final String MANAGED = "managed"; + static final String STORAGE_HOST= "storageHost"; + static final String MANAGED_STORE_TARGET = "managedStoreTarget"; + static final String MANAGED_STORE_TARGET_ROOT_VOLUME = "managedStoreTargetRootVolume"; + static final String CHAP_INITIATOR_USERNAME = "chapInitiatorUsername"; + static final String CHAP_INITIATOR_SECRET = "chapInitiatorSecret"; + static final String CHAP_TARGET_USERNAME = "chapTargetUsername"; + static final String CHAP_TARGET_SECRET = "chapTargetSecret"; + static final String VOLUME_SIZE = "volumeSize"; + boolean isHypervisorSupported(HypervisorType hypervisor); boolean isLocalStorageSupported(); @@ -37,5 +49,11 @@ public interface PrimaryDataStoreInfo extends StoragePool { @Override StoragePoolType getPoolType(); + boolean isManaged(); + + void setDetails(Map details); + + Map getDetails(); + PrimaryDataStoreLifeCycle getLifeCycle(); } diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java index 3f676ae73dc..b6e61069e77 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java @@ -78,7 +78,11 @@ public interface VolumeService { VolumeEntity getVolumeEntity(long volumeId); - AsyncCallFuture createVolumeFromTemplateAsync(VolumeInfo volume, long dataStoreId, TemplateInfo template); + AsyncCallFuture createManagedStorageAndVolumeFromTemplateAsync(VolumeInfo volumeInfo, long destDataStoreId, + TemplateInfo srcTemplateInfo, long destHostId); + + AsyncCallFuture createVolumeFromTemplateAsync(VolumeInfo volume, long dataStoreId, + TemplateInfo template); AsyncCallFuture copyVolume(VolumeInfo srcVolume, DataStore destStore); 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 d3eda8a3721..4d0c7872c03 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.DataStore; 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.SnapshotDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotService; @@ -1113,7 +1114,23 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati future = volService.createVolumeAsync(volume, destPool); } else { TemplateInfo templ = tmplFactory.getTemplate(templateId, DataStoreRole.Image); - future = volService.createVolumeFromTemplateAsync(volume, destPool.getId(), templ); + + PrimaryDataStore primaryDataStore = (PrimaryDataStore)destPool; + + if (primaryDataStore.isManaged()) { + DiskOffering diskOffering = _entityMgr.findById(DiskOffering.class, volume.getDiskOfferingId()); + HypervisorType hyperType = vm.getVirtualMachine().getHypervisorType(); + + // update the volume's hypervisor_ss_reserve from its disk offering (used for managed storage) + updateHypervisorSnapshotReserveForVolume(diskOffering, volume, hyperType); + + long hostId = vm.getVirtualMachine().getHostId(); + + future = volService.createManagedStorageAndVolumeFromTemplateAsync(volume, destPool.getId(), templ, hostId); + } + else { + future = volService.createVolumeFromTemplateAsync(volume, destPool.getId(), templ); + } } VolumeApiResult result = null; try { diff --git a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java index 0556479e072..df81199461e 100644 --- a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java +++ b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java @@ -46,6 +46,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.storage.RemoteHostEndPoint; import org.apache.cloudstack.storage.command.CopyCommand; import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity; @@ -94,15 +95,17 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { protected boolean needCacheStorage(DataObject srcData, DataObject destData) { DataTO srcTO = srcData.getTO(); - DataTO destTO = destData.getTO(); DataStoreTO srcStoreTO = srcTO.getDataStore(); - DataStoreTO destStoreTO = destTO.getDataStore(); + if (srcStoreTO instanceof NfsTO || srcStoreTO.getRole() == DataStoreRole.ImageCache) { //|| // (srcStoreTO instanceof PrimaryDataStoreTO && ((PrimaryDataStoreTO)srcStoreTO).getPoolType() == StoragePoolType.NetworkFilesystem)) { return false; } + DataTO destTO = destData.getTO(); + DataStoreTO destStoreTO = destTO.getDataStore(); + if (destStoreTO instanceof NfsTO || destStoreTO.getRole() == DataStoreRole.ImageCache) { return false; } @@ -147,7 +150,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { return selectedScope; } - protected Answer copyObject(DataObject srcData, DataObject destData) { + protected Answer copyObject(DataObject srcData, DataObject destData, Host destHost) { String value = configDao.getValue(Config.PrimaryStorageDownloadWait.toString()); int _primaryStorageDownloadWait = NumbersUtil.parseInt(value, Integer.parseInt(Config.PrimaryStorageDownloadWait.getDefaultValue())); Answer answer = null; @@ -160,7 +163,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { } CopyCommand cmd = new CopyCommand(srcForCopy.getTO(), destData.getTO(), _primaryStorageDownloadWait, VirtualMachineManager.ExecuteInSequence.value()); - EndPoint ep = selector.select(srcForCopy, destData); + EndPoint ep = destHost != null ? RemoteHostEndPoint.getHypervisorHostEndPoint(destHost) : selector.select(srcForCopy, destData); if (ep == null) { String errMsg = "No remote endpoint to send command, check if host or ssvm is down?"; s_logger.error(errMsg); @@ -193,6 +196,10 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { } } + protected Answer copyObject(DataObject srcData, DataObject destData) { + return copyObject(srcData, destData, null); + } + protected DataObject cacheSnapshotChain(SnapshotInfo snapshot, Scope scope) { DataObject leafData = null; DataStore store = cacheMgr.getCacheStorage(snapshot, scope); @@ -392,8 +399,9 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { return answer; } + // Note: destHost is currently only used if the copyObject method is invoked @Override - public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { + public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback callback) { Answer answer = null; String errMsg = null; try { @@ -416,7 +424,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { } else if (srcData.getType() == DataObjectType.SNAPSHOT && destData.getType() == DataObjectType.SNAPSHOT) { answer = copySnapshot(srcData, destData); } else { - answer = copyObject(srcData, destData); + answer = copyObject(srcData, destData, destHost); } if (answer != null && !answer.getResult()) { @@ -432,6 +440,11 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { return null; } + @Override + public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { + return copyAsync(srcData, destData, null, callback); + } + @DB protected Answer createTemplateFromSnapshot(DataObject srcData, DataObject destData) { diff --git a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java index 37e8baabbe9..eed1e08005f 100644 --- a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java +++ b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java @@ -46,7 +46,7 @@ public class DataMotionServiceImpl implements DataMotionService { StorageStrategyFactory storageStrategyFactory; @Override - public void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { + public void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback callback) { if (srcData.getDataStore() == null || destData.getDataStore() == null) { throw new CloudRuntimeException("can't find data store"); } @@ -65,7 +65,12 @@ public class DataMotionServiceImpl implements DataMotionService { destData.getType().name() + " '" + destData.getUuid() + "'"); } - strategy.copyAsync(srcData, destData, callback); + strategy.copyAsync(srcData, destData, destHost, callback); + } + + @Override + public void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { + copyAsync(srcData, destData, null, callback); } @Override diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java index 8db21ccbf7a..34db48146be 100644 --- a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java +++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java @@ -30,6 +30,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; import org.apache.cloudstack.storage.command.CopyCmdAnswer; import org.apache.cloudstack.storage.datastore.ObjectInDataStoreManager; @@ -299,6 +300,20 @@ public class TemplateObject implements TemplateInfo { if (dataStore == null) { return null; } + + // managed primary data stores should not have an install path + if (dataStore instanceof PrimaryDataStore) { + PrimaryDataStore primaryDataStore = (PrimaryDataStore)dataStore; + + Map details = primaryDataStore.getDetails(); + + boolean managed = details != null && Boolean.parseBoolean(details.get(PrimaryDataStore.MANAGED)); + + if (managed) { + return null; + } + } + DataObjectInStore obj = objectInStoreMgr.findObject(this, dataStore); return obj.getInstallPath(); } diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java index 2a11e1337e6..accfeb5318e 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java @@ -57,6 +57,11 @@ public class MockStorageMotionStrategy implements DataMotionStrategy { return StrategyPriority.HIGHEST; } + @Override + public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback callback) { + throw new UnsupportedOperationException(); + } + @Override public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { CopyCmdAnswer answer = null; diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java index 649e6896c8e..4fb37e2a181 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java @@ -20,6 +20,7 @@ import java.io.File; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Map; import javax.inject.Inject; @@ -67,6 +68,7 @@ import com.cloud.utils.storage.encoding.EncodingType; public class PrimaryDataStoreImpl implements PrimaryDataStore { private static final Logger s_logger = Logger.getLogger(PrimaryDataStoreImpl.class); + protected PrimaryDataStoreDriver driver; protected StoragePoolVO pdsv; @Inject @@ -86,6 +88,7 @@ public class PrimaryDataStoreImpl implements PrimaryDataStore { @Inject private VolumeDao volumeDao; + private Map _details; public PrimaryDataStoreImpl() { @@ -135,6 +138,16 @@ public class PrimaryDataStoreImpl implements PrimaryDataStore { return pdsv.getId(); } + @Override + public void setDetails(Map details) { + _details = details; + } + + @Override + public Map getDetails() { + return _details; + } + @Override public String getUri() { String path = pdsv.getPath().replaceFirst("/*", ""); @@ -222,10 +235,15 @@ public class PrimaryDataStoreImpl implements PrimaryDataStore { return null; } + @Override + public boolean isManaged() { + return pdsv.isManaged(); + } + @Override public DataObject create(DataObject obj) { // create template on primary storage - if (obj.getType() == DataObjectType.TEMPLATE) { + if (obj.getType() == DataObjectType.TEMPLATE && !isManaged()) { try { String templateIdPoolIdString = "templateId:" + obj.getId() + "poolId:" + getId(); VMTemplateStoragePoolVO templateStoragePoolRef; diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/DefaultHostListener.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/DefaultHostListener.java index 4838bf678e0..fffd1e815c4 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/DefaultHostListener.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/DefaultHostListener.java @@ -85,7 +85,7 @@ public class DefaultHostListener implements HypervisorHostListener { poolVO.setCapacityBytes(mspAnswer.getPoolInfo().getCapacityBytes()); primaryStoreDao.update(pool.getId(), poolVO); - s_logger.info("Connection established between " + pool + " host + " + hostId); + s_logger.info("Connection established between storage pool " + pool + " and host " + hostId); return true; } diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java index fa0fd956a7a..d47ee27d4a5 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java @@ -20,6 +20,7 @@ package org.apache.cloudstack.storage.volume; import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -40,6 +41,7 @@ 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.ObjectInDataStoreStateMachine; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService.VolumeApiResult; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; import org.apache.cloudstack.engine.subsystem.api.storage.Scope; @@ -60,6 +62,7 @@ import org.apache.cloudstack.storage.datastore.PrimaryDataStoreProviderManager; import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao; import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreVO; import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.cloudstack.storage.to.TemplateObjectTO; import com.cloud.agent.api.Answer; import com.cloud.agent.api.storage.ListVolumeAnswer; @@ -72,6 +75,7 @@ import com.cloud.event.EventTypes; import com.cloud.event.UsageEventUtils; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.ResourceAllocationException; +import com.cloud.host.dao.HostDao; import com.cloud.host.Host; import com.cloud.storage.DataStoreRole; import com.cloud.storage.ScopeType; @@ -122,6 +126,8 @@ public class VolumeServiceImpl implements VolumeService { VolumeDao _volumeDao; @Inject EndPointSelector _epSelector; + @Inject + HostDao _hostDao; public VolumeServiceImpl() { } @@ -349,6 +355,39 @@ public class VolumeServiceImpl implements VolumeService { return null; } + private class ManagedCreateBaseImageContext extends AsyncRpcContext { + private final VolumeInfo _volumeInfo; + private final PrimaryDataStore _primaryDataStore; + private final TemplateInfo _templateInfo; + private final AsyncCallFuture _future; + + public ManagedCreateBaseImageContext(AsyncCompletionCallback callback, VolumeInfo volumeInfo, + PrimaryDataStore primaryDatastore, TemplateInfo templateInfo, AsyncCallFuture future) { + super(callback); + + _volumeInfo = volumeInfo; + _primaryDataStore = primaryDatastore; + _templateInfo = templateInfo; + _future = future; + } + + public VolumeInfo getVolumeInfo() { + return _volumeInfo; + } + + public PrimaryDataStore getPrimaryDataStore() { + return _primaryDataStore; + } + + public TemplateInfo getTemplateInfo() { + return _templateInfo; + } + + public AsyncCallFuture getFuture() { + return _future; + } + } + class CreateBaseImageContext extends AsyncRpcContext { private final VolumeInfo volume; private final PrimaryDataStore dataStore; @@ -411,7 +450,6 @@ public class VolumeServiceImpl implements VolumeService { @DB protected void createBaseImageAsync(VolumeInfo volume, PrimaryDataStore dataStore, TemplateInfo template, AsyncCallFuture future) { - DataObject templateOnPrimaryStoreObj = dataStore.create(template); VMTemplateStoragePoolVO templatePoolRef = _tmpltPoolDao.findByPoolTemplate(dataStore.getId(), template.getId()); @@ -476,6 +514,37 @@ public class VolumeServiceImpl implements VolumeService { return; } + protected Void managedCopyBaseImageCallback(AsyncCallbackDispatcher callback, + ManagedCreateBaseImageContext context) { + CopyCommandResult result = callback.getResult(); + VolumeInfo volumeInfo = context.getVolumeInfo(); + VolumeApiResult res = new VolumeApiResult(volumeInfo); + + if (result.isSuccess()) { + // volumeInfo.processEvent(Event.OperationSuccessed, result.getAnswer()); + + VolumeVO volume = volDao.findById(volumeInfo.getId()); + CopyCmdAnswer answer = (CopyCmdAnswer)result.getAnswer(); + TemplateObjectTO templateObjectTo = (TemplateObjectTO)answer.getNewData(); + + volume.setPath(templateObjectTo.getPath()); + volume.setFormat(templateObjectTo.getFormat()); + + volDao.update(volume.getId(), volume); + } + else { + volumeInfo.processEvent(Event.DestroyRequested); + + res.setResult(result.getResult()); + } + + AsyncCallFuture future = context.getFuture(); + + future.complete(res); + + return null; + } + @DB protected Void copyBaseImageCallback(AsyncCallbackDispatcher callback, CreateBaseImageContext context) { CopyCommandResult result = callback.getResult(); @@ -577,6 +646,91 @@ public class VolumeServiceImpl implements VolumeService { return null; } + @Override + public AsyncCallFuture createManagedStorageAndVolumeFromTemplateAsync(VolumeInfo volumeInfo, long destDataStoreId, + TemplateInfo srcTemplateInfo, long destHostId) { + PrimaryDataStore destPrimaryDataStore = dataStoreMgr.getPrimaryDataStore(destDataStoreId); + TemplateInfo destTemplateInfo = (TemplateInfo)destPrimaryDataStore.create(srcTemplateInfo); + Host destHost = _hostDao.findById(destHostId); + + if (destHost == null) { + throw new CloudRuntimeException("Destinatin host should not be null."); + } + + AsyncCallFuture future = new AsyncCallFuture(); + + try { + // must call driver to have a volume created + AsyncCallFuture createVolumeFuture = createVolumeAsync(volumeInfo, destPrimaryDataStore); + + VolumeApiResult createVolumeResult = createVolumeFuture.get(); + + if (createVolumeResult.isFailed()) { + throw new CloudRuntimeException("Creation of a volume failed: " + createVolumeResult.getResult()); + } + + // refresh the volume from the DB + volumeInfo = volFactory.getVolume(volumeInfo.getId(), destPrimaryDataStore); + + connectVolumeToHost(volumeInfo, destHost, destPrimaryDataStore); + + ManagedCreateBaseImageContext context = new ManagedCreateBaseImageContext(null, volumeInfo, + destPrimaryDataStore, srcTemplateInfo, future); + AsyncCallbackDispatcher caller = AsyncCallbackDispatcher.create(this); + caller.setCallback(caller.getTarget().managedCopyBaseImageCallback(null, null)).setContext(context); + + Map details = new HashMap(); + + details.put(PrimaryDataStore.MANAGED, Boolean.TRUE.toString()); + details.put(PrimaryDataStore.STORAGE_HOST, destPrimaryDataStore.getHostAddress()); + // for managed storage, the storage repository (XenServer) or datastore (ESX) name is based off of the iScsiName property of a volume + details.put(PrimaryDataStore.MANAGED_STORE_TARGET, volumeInfo.get_iScsiName()); + details.put(PrimaryDataStore.MANAGED_STORE_TARGET_ROOT_VOLUME, volumeInfo.getName()); + details.put(PrimaryDataStore.VOLUME_SIZE, String.valueOf(volumeInfo.getSize())); + + ChapInfo chapInfo = getChapInfo(volumeInfo, destPrimaryDataStore); + + if (chapInfo != null) { + details.put(PrimaryDataStore.CHAP_INITIATOR_USERNAME, chapInfo.getInitiatorUsername()); + details.put(PrimaryDataStore.CHAP_INITIATOR_SECRET, chapInfo.getInitiatorSecret()); + details.put(PrimaryDataStore.CHAP_TARGET_USERNAME, chapInfo.getTargetUsername()); + details.put(PrimaryDataStore.CHAP_TARGET_SECRET, chapInfo.getTargetSecret()); + } + + destPrimaryDataStore.setDetails(details); + + motionSrv.copyAsync(srcTemplateInfo, destTemplateInfo, destHost, caller); + } + catch (Throwable t) { + String errMsg = t.toString(); + + volumeInfo.processEvent(Event.DestroyRequested); + + disconnectVolumeFromHost(volumeInfo, destHost, destPrimaryDataStore); + + try { + AsyncCallFuture expungeVolumeFuture = expungeVolumeAsync(volumeInfo); + + VolumeApiResult expungeVolumeResult = expungeVolumeFuture.get(); + + if (expungeVolumeResult.isFailed()) { + errMsg += " : Failed to expunge a volume that was created"; + } + } + catch (Exception ex) { + errMsg += " : " + ex.getMessage(); + } + + VolumeApiResult result = new VolumeApiResult(volumeInfo); + + result.setResult(errMsg); + + future.complete(result); + } + + return future; + } + @DB @Override public AsyncCallFuture createVolumeFromTemplateAsync(VolumeInfo volume, long dataStoreId, TemplateInfo template) { diff --git a/plugins/hypervisors/simulator/src/org/apache/cloudstack/storage/motion/SimulatorDataMotionStrategy.java b/plugins/hypervisors/simulator/src/org/apache/cloudstack/storage/motion/SimulatorDataMotionStrategy.java index 3eb2cf60b7d..40909629c70 100644 --- a/plugins/hypervisors/simulator/src/org/apache/cloudstack/storage/motion/SimulatorDataMotionStrategy.java +++ b/plugins/hypervisors/simulator/src/org/apache/cloudstack/storage/motion/SimulatorDataMotionStrategy.java @@ -42,6 +42,11 @@ public class SimulatorDataMotionStrategy implements DataMotionStrategy { return StrategyPriority.HYPERVISOR; } + @Override + public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback callback) { + throw new UnsupportedOperationException(); + } + @Override public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { CopyCommandResult result = new CopyCommandResult("something", null); diff --git a/plugins/hypervisors/vmware/src/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java b/plugins/hypervisors/vmware/src/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java index 09f10688a68..24efde79a99 100644 --- a/plugins/hypervisors/vmware/src/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java +++ b/plugins/hypervisors/vmware/src/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java @@ -85,6 +85,11 @@ public class VmwareStorageMotionStrategy implements DataMotionStrategy { return StrategyPriority.CANT_HANDLE; } + @Override + public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback callback) { + throw new UnsupportedOperationException(); + } + @Override public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { CopyCommandResult result = new CopyCommandResult(null, null); diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index c0d691ad5ba..2b441e559ba 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -1869,18 +1869,23 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return prepareManagedStorage(conn, details, null, vdiNameLabel); } - protected VDI prepareManagedStorage(Connection conn, Map details, String path, String vdiNameLabel) throws Exception { + protected SR prepareManagedSr(Connection conn, Map details) { String iScsiName = details.get(DiskTO.IQN); String storageHost = details.get(DiskTO.STORAGE_HOST); String chapInitiatorUsername = details.get(DiskTO.CHAP_INITIATOR_USERNAME); String chapInitiatorSecret = details.get(DiskTO.CHAP_INITIATOR_SECRET); - Long volumeSize = Long.parseLong(details.get(DiskTO.VOLUME_SIZE)); - SR sr = getIscsiSR(conn, iScsiName, storageHost, iScsiName, chapInitiatorUsername, chapInitiatorSecret, true); + return getIscsiSR(conn, iScsiName, storageHost, iScsiName, chapInitiatorUsername, chapInitiatorSecret, true); + } + + protected VDI prepareManagedStorage(Connection conn, Map details, String path, String vdiNameLabel) throws Exception { + SR sr = prepareManagedSr(conn, details); VDI vdi = getVDIbyUuid(conn, path, false); if (vdi == null) { + Long volumeSize = Long.parseLong(details.get(DiskTO.VOLUME_SIZE)); + vdi = createVdi(sr, vdiNameLabel, volumeSize); } diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java index f2abff74f80..e512046b35b 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java @@ -59,6 +59,7 @@ import org.apache.cloudstack.storage.command.ForgetObjectCmd; import org.apache.cloudstack.storage.command.IntroduceObjectAnswer; import org.apache.cloudstack.storage.command.IntroduceObjectCmd; import org.apache.cloudstack.storage.datastore.protocol.DataStoreProtocol; +import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; import org.apache.cloudstack.storage.to.SnapshotObjectTO; import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.cloudstack.storage.to.VolumeObjectTO; @@ -887,58 +888,121 @@ public class XenServerStorageProcessor implements StorageProcessor { @Override public Answer copyTemplateToPrimaryStorage(CopyCommand cmd) { - DataTO srcData = cmd.getSrcTO(); - DataTO destData = cmd.getDestTO(); + DataTO srcDataTo = cmd.getSrcTO(); + DataTO destDataTo = cmd.getDestTO(); int wait = cmd.getWait(); - DataStoreTO srcStore = srcData.getDataStore(); - try { - if ((srcStore instanceof NfsTO) && (srcData.getObjectType() == DataObjectType.TEMPLATE)) { - NfsTO srcImageStore = (NfsTO)srcStore; - TemplateObjectTO srcTemplate = (TemplateObjectTO)srcData; - String storeUrl = srcImageStore.getUrl(); + DataStoreTO srcDataStoreTo = srcDataTo.getDataStore(); + try { + if ((srcDataStoreTo instanceof NfsTO) && (srcDataTo.getObjectType() == DataObjectType.TEMPLATE)) { + NfsTO srcImageStore = (NfsTO)srcDataStoreTo; + TemplateObjectTO srcTemplateObjectTo = (TemplateObjectTO)srcDataTo; + String storeUrl = srcImageStore.getUrl(); URI uri = new URI(storeUrl); - String tmplpath = uri.getHost() + ":" + uri.getPath() + "/" + srcData.getPath(); - String poolName = destData.getDataStore().getUuid(); + String tmplPath = uri.getHost() + ":" + uri.getPath() + "/" + srcDataTo.getPath(); + DataStoreTO destDataStoreTo = destDataTo.getDataStore(); + + boolean managed = false; + String storageHost = null; + String managedStoragePoolName = null; + String managedStoragePoolRootVolumeName = null; + String managedStoragePoolRootVolumeSize = null; + String chapInitiatorUsername = null; + String chapInitiatorSecret = null; + + if (destDataStoreTo instanceof PrimaryDataStoreTO) { + PrimaryDataStoreTO destPrimaryDataStoreTo = (PrimaryDataStoreTO)destDataStoreTo; + + Map details = destPrimaryDataStoreTo.getDetails(); + + if (details != null) { + managed = Boolean.parseBoolean(details.get(PrimaryDataStoreTO.MANAGED)); + + if (managed) { + storageHost = details.get(PrimaryDataStoreTO.STORAGE_HOST); + managedStoragePoolName = details.get(PrimaryDataStoreTO.MANAGED_STORE_TARGET); + managedStoragePoolRootVolumeName = details.get(PrimaryDataStoreTO.MANAGED_STORE_TARGET_ROOT_VOLUME); + managedStoragePoolRootVolumeSize = details.get(PrimaryDataStoreTO.VOLUME_SIZE); + chapInitiatorUsername = details.get(PrimaryDataStoreTO.CHAP_INITIATOR_USERNAME); + chapInitiatorSecret = details.get(PrimaryDataStoreTO.CHAP_INITIATOR_SECRET); + } + } + } + Connection conn = hypervisorResource.getConnection(); - SR poolsr = null; - Set srs = SR.getByNameLabel(conn, poolName); - if (srs.size() != 1) { - String msg = "There are " + srs.size() + " SRs with same name: " + poolName; - s_logger.warn(msg); - return new CopyCmdAnswer(msg); - } else { - poolsr = srs.iterator().next(); + final SR sr; + + if (managed) { + Map details = new HashMap(); + + details.put(DiskTO.STORAGE_HOST, storageHost); + details.put(DiskTO.IQN, managedStoragePoolName); + details.put(DiskTO.VOLUME_SIZE, managedStoragePoolRootVolumeSize); + details.put(DiskTO.CHAP_INITIATOR_USERNAME, chapInitiatorUsername); + details.put(DiskTO.CHAP_INITIATOR_SECRET, chapInitiatorSecret); + + sr = hypervisorResource.prepareManagedSr(conn, details); } - String pUuid = poolsr.getUuid(conn); - boolean isISCSI = IsISCSI(poolsr.getType(conn)); - String uuid = copy_vhd_from_secondarystorage(conn, tmplpath, pUuid, wait); - VDI tmpl = getVDIbyUuid(conn, uuid); - VDI snapshotvdi = tmpl.snapshot(conn, new HashMap()); - String snapshotUuid = snapshotvdi.getUuid(conn); - snapshotvdi.setNameLabel(conn, "Template " + srcTemplate.getName()); - String parentuuid = getVhdParent(conn, pUuid, snapshotUuid, isISCSI); - VDI parent = getVDIbyUuid(conn, parentuuid); - Long phySize = parent.getPhysicalUtilisation(conn); - tmpl.destroy(conn); - poolsr.scan(conn); + else { + String srName = destDataStoreTo.getUuid(); + Set srs = SR.getByNameLabel(conn, srName); + + if (srs.size() != 1) { + String msg = "There are " + srs.size() + " SRs with same name: " + srName; + + s_logger.warn(msg); + + return new CopyCmdAnswer(msg); + } else { + sr = srs.iterator().next(); + } + } + + String srUuid = sr.getUuid(conn); + String tmplUuid = copy_vhd_from_secondarystorage(conn, tmplPath, srUuid, wait); + VDI tmplVdi = getVDIbyUuid(conn, tmplUuid); + + final String uuidToReturn; + + if (managed) { + uuidToReturn = tmplUuid; + + tmplVdi.setNameLabel(conn, managedStoragePoolRootVolumeName); + } + else { + VDI snapshotVdi = tmplVdi.snapshot(conn, new HashMap()); + + uuidToReturn = snapshotVdi.getUuid(conn); + + snapshotVdi.setNameLabel(conn, "Template " + srcTemplateObjectTo.getName()); + + tmplVdi.destroy(conn); + } + + sr.scan(conn); + try { Thread.sleep(5000); } catch (Exception e) { } TemplateObjectTO newVol = new TemplateObjectTO(); - newVol.setUuid(snapshotvdi.getUuid(conn)); - newVol.setPath(newVol.getUuid()); + + newVol.setUuid(uuidToReturn); + newVol.setPath(uuidToReturn); newVol.setFormat(ImageFormat.VHD); + return new CopyCmdAnswer(newVol); } } catch (Exception e) { String msg = "Catch Exception " + e.getClass().getName() + " for template + " + " due to " + e.toString(); + s_logger.warn(msg, e); + return new CopyCmdAnswer(msg); } + return new CopyCmdAnswer("not implemented yet"); } diff --git a/plugins/hypervisors/xen/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java b/plugins/hypervisors/xen/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java index e8217f7216f..975deec531f 100644 --- a/plugins/hypervisors/xen/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java +++ b/plugins/hypervisors/xen/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java @@ -90,6 +90,11 @@ public class XenServerStorageMotionStrategy implements DataMotionStrategy { return StrategyPriority.CANT_HANDLE; } + @Override + public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback callback) { + throw new UnsupportedOperationException(); + } + @Override public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { CopyCommandResult result = new CopyCommandResult(null, null); diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index 283aeeaf5a5..08cc5a63f50 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -2016,15 +2016,54 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati return createServiceOffering(userId, cmd.getIsSystem(), vmType, cmd.getServiceOfferingName(), cpuNumber, memory, cpuSpeed, cmd.getDisplayText(), localStorageRequired, offerHA, limitCpuUse, volatileVm, cmd.getTags(), cmd.getDomainId(), cmd.getHostTag(), cmd.getNetworkRate(), cmd.getDeploymentPlanner(), cmd.getDetails(), - cmd.getBytesReadRate(), cmd.getBytesWriteRate(), cmd.getIopsReadRate(), cmd.getIopsWriteRate()); + cmd.isCustomizedIops(), cmd.getMinIops(), cmd.getMaxIops(), cmd.getBytesReadRate(), cmd.getBytesWriteRate(), cmd.getIopsReadRate(), cmd.getIopsWriteRate(), + cmd.getHypervisorSnapshotReserve()); } protected ServiceOfferingVO createServiceOffering(long userId, boolean isSystem, VirtualMachine.Type vmType, String name, Integer cpu, Integer ramSize, Integer speed, String displayText, boolean localStorageRequired, boolean offerHA, boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, String hostTag, - Integer networkRate, String deploymentPlanner, Map details, Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate) { + Integer networkRate, String deploymentPlanner, Map details, Boolean isCustomizedIops, Long minIops, Long maxIops, + Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate, Integer hypervisorSnapshotReserve) { tags = StringUtils.cleanupTags(tags); ServiceOfferingVO offering = new ServiceOfferingVO(name, cpu, ramSize, speed, networkRate, null, offerHA, limitResourceUse, volatileVm, displayText, localStorageRequired, false, tags, isSystem, vmType, domainId, hostTag, deploymentPlanner); + + if (isCustomizedIops != null) { + bytesReadRate = null; + bytesWriteRate = null; + iopsReadRate = null; + iopsWriteRate = null; + + if (isCustomizedIops) { + minIops = null; + maxIops = null; + } else { + if (minIops == null && maxIops == null) { + minIops = 0L; + maxIops = 0L; + } else { + if (minIops == null || minIops <= 0) { + throw new InvalidParameterValueException("The min IOPS must be greater than 0."); + } + + if (maxIops == null) { + maxIops = 0L; + } + + if (minIops > maxIops) { + throw new InvalidParameterValueException("The min IOPS must be less than or equal to the max IOPS."); + } + } + } + } else { + minIops = null; + maxIops = null; + } + + offering.setCustomizedIops(isCustomizedIops); + offering.setMinIops(minIops); + offering.setMaxIops(maxIops); + if ((bytesReadRate != null) && (bytesReadRate > 0)) offering.setBytesReadRate(bytesReadRate); if ((bytesWriteRate != null) && (bytesWriteRate > 0)) @@ -2034,6 +2073,12 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if ((iopsWriteRate != null) && (iopsWriteRate > 0)) offering.setIopsWriteRate(iopsWriteRate); + if (hypervisorSnapshotReserve != null && hypervisorSnapshotReserve < 0) { + throw new InvalidParameterValueException("If provided, Hypervisor Snapshot Reserve must be greater than or equal to 0."); + } + + offering.setHypervisorSnapshotReserve(hypervisorSnapshotReserve); + if ((offering = _serviceOfferingDao.persist(offering)) != null) { if (details != null) { List detailsVO = new ArrayList(); diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js index 869b876865c..cb54c318135 100644 --- a/ui/scripts/configuration.js +++ b/ui/scripts/configuration.js @@ -136,6 +136,113 @@ number: true } }, + qosType: { + label: 'label.qos.type', + docID: 'helpDiskOfferingQoSType', + select: function(args) { + var items = []; + items.push({ + id: '', + description: '' + }); + items.push({ + id: 'hypervisor', + description: 'hypervisor' + }); + items.push({ + id: 'storage', + description: 'storage' + }); + args.response.success({ + data: items + }); + + args.$select.change(function() { + var $form = $(this).closest('form'); + var $isCustomizedIops = $form.find('.form-item[rel=isCustomizedIops]'); + var $minIops = $form.find('.form-item[rel=minIops]'); + var $maxIops = $form.find('.form-item[rel=maxIops]'); + var $hypervisorSnapshotReserve = $form.find('.form-item[rel=hypervisorSnapshotReserve]'); + var $diskBytesReadRate = $form.find('.form-item[rel=diskBytesReadRate]'); + var $diskBytesWriteRate = $form.find('.form-item[rel=diskBytesWriteRate]'); + var $diskIopsReadRate = $form.find('.form-item[rel=diskIopsReadRate]'); + var $diskIopsWriteRate = $form.find('.form-item[rel=diskIopsWriteRate]'); + + var qosId = $(this).val(); + + if (qosId == 'storage') { // Storage QoS + $diskBytesReadRate.hide(); + $diskBytesWriteRate.hide(); + $diskIopsReadRate.hide(); + $diskIopsWriteRate.hide(); + + $isCustomizedIops.css('display', 'inline-block'); + + if ($isCustomizedIops.find('input[type=checkbox]').is(':checked')) { + $minIops.hide(); + $maxIops.hide(); + } else { + $minIops.css('display', 'inline-block'); + $maxIops.css('display', 'inline-block'); + } + + $hypervisorSnapshotReserve.css('display', 'inline-block'); + } else if (qosId == 'hypervisor') { // Hypervisor Qos + $isCustomizedIops.hide(); + $minIops.hide(); + $maxIops.hide(); + $hypervisorSnapshotReserve.hide(); + + $diskBytesReadRate.css('display', 'inline-block'); + $diskBytesWriteRate.css('display', 'inline-block'); + $diskIopsReadRate.css('display', 'inline-block'); + $diskIopsWriteRate.css('display', 'inline-block'); + } else { // No Qos + $diskBytesReadRate.hide(); + $diskBytesWriteRate.hide(); + $diskIopsReadRate.hide(); + $diskIopsWriteRate.hide(); + $isCustomizedIops.hide(); + $minIops.hide(); + $maxIops.hide(); + $hypervisorSnapshotReserve.hide(); + } + }); + } + }, + isCustomizedIops: { + label: 'label.custom.disk.iops', + docID: 'helpDiskOfferingCustomDiskIops', + isBoolean: true, + isReverse: true, + isChecked: false + }, + minIops: { + label: 'label.disk.iops.min', + docID: 'helpDiskOfferingDiskIopsMin', + dependsOn: 'isCustomizedIops', + validation: { + required: false, + number: true + } + }, + maxIops: { + label: 'label.disk.iops.max', + docID: 'helpDiskOfferingDiskIopsMax', + dependsOn: 'isCustomizedIops', + validation: { + required: false, + number: true + } + }, + hypervisorSnapshotReserve: { + label: 'label.hypervisor.snapshot.reserve', + docID: 'helpDiskOfferingHypervisorSnapshotReserve', + validation: { + required: false, + number: true + } + }, diskBytesReadRate: { label: 'label.disk.bytes.read.rate', docID: 'helpComputeOfferingDiskBytesReadRate', @@ -326,26 +433,59 @@ networkrate: args.data.networkRate }); } - if (args.data.diskBytesReadRate != null && args.data.diskBytesReadRate.length > 0) { + + if (args.data.qosType == 'storage') { + var customIops = args.data.isCustomizedIops == "on"; + $.extend(data, { - bytesreadrate: args.data.diskBytesReadRate - }); - } - if (args.data.diskBytesWriteRate != null && args.data.diskBytesWriteRate.length > 0) { - $.extend(data, { - byteswriterate: args.data.diskBytesWriteRate - }); - } - if (args.data.diskIopsReadRate != null && args.data.diskIopsReadRate.length > 0) { - $.extend(data, { - iopsreadrate: args.data.diskIopsReadRate - }); - } - if (args.data.diskIopsWriteRate != null && args.data.diskIopsWriteRate.length > 0) { - $.extend(data, { - iopswriterate: args.data.diskIopsWriteRate + customizediops: customIops }); + + if (!customIops) { + if (args.data.minIops != null && args.data.minIops.length > 0) { + $.extend(data, { + miniops: args.data.minIops + }); + } + + if (args.data.maxIops != null && args.data.maxIops.length > 0) { + $.extend(data, { + maxiops: args.data.maxIops + }); + } + } + + if (args.data.hypervisorSnapshotReserve != null && args.data.hypervisorSnapshotReserve.length > 0) { + $.extend(data, { + hypervisorsnapshotreserve: args.data.hypervisorSnapshotReserve + }); + } + } else if (args.data.qosType == 'hypervisor') { + if (args.data.diskBytesReadRate != null && args.data.diskBytesReadRate.length > 0) { + $.extend(data, { + bytesreadrate: args.data.diskBytesReadRate + }); + } + + if (args.data.diskBytesWriteRate != null && args.data.diskBytesWriteRate.length > 0) { + $.extend(data, { + byteswriterate: args.data.diskBytesWriteRate + }); + } + + if (args.data.diskIopsReadRate != null && args.data.diskIopsReadRate.length > 0) { + $.extend(data, { + iopsreadrate: args.data.diskIopsReadRate + }); + } + + if (args.data.diskIopsWriteRate != null && args.data.diskIopsWriteRate.length > 0) { + $.extend(data, { + iopswriterate: args.data.diskIopsWriteRate + }); + } } + $.extend(data, { offerha: (args.data.offerHA == "on") });