From c344693e48d80313270d1a972b0a3badf315567d Mon Sep 17 00:00:00 2001 From: Mike Tutkowski Date: Tue, 17 Jun 2014 19:22:52 -0600 Subject: [PATCH] Inform the applicable storage plug-in's life cycle that capacity (bytes and/or IOPS) can be updated --- .../storage/PrimaryDataStoreLifeCycle.java | 8 + .../com/cloud/capacity/CapacityManager.java | 4 + .../ElastistorPrimaryDataStoreLifeCycle.java | 4 +- ...oudStackPrimaryDataStoreLifeCycleImpl.java | 3 + .../NexentaPrimaryDataStoreLifeCycle.java | 5 + .../SamplePrimaryDataStoreLifeCycleImpl.java | 4 + .../SolidFirePrimaryDataStoreLifeCycle.java | 48 ++++-- ...idFireSharedPrimaryDataStoreLifeCycle.java | 59 ++++++- .../storage/datastore/util/SolidFireUtil.java | 144 +++++++++++++++++- .../cloud/capacity/CapacityManagerImpl.java | 18 ++- .../com/cloud/storage/StorageManagerImpl.java | 39 ++--- ui/scripts/sharedFunctions.js | 34 +++++ ui/scripts/system.js | 10 ++ 13 files changed, 341 insertions(+), 39 deletions(-) diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreLifeCycle.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreLifeCycle.java index 28b06f5234c..7640cf33abc 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreLifeCycle.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreLifeCycle.java @@ -18,5 +18,13 @@ */ package org.apache.cloudstack.engine.subsystem.api.storage; +import java.util.Map; + +import com.cloud.storage.StoragePool; + public interface PrimaryDataStoreLifeCycle extends DataStoreLifeCycle { + public static final String CAPACITY_BYTES = "capacityBytes"; + public static final String CAPACITY_IOPS = "capacityIops"; + + void updateStoragePool(StoragePool storagePool, Map details); } diff --git a/engine/components-api/src/com/cloud/capacity/CapacityManager.java b/engine/components-api/src/com/cloud/capacity/CapacityManager.java index 038a2c05f01..51f73326d53 100755 --- a/engine/components-api/src/com/cloud/capacity/CapacityManager.java +++ b/engine/components-api/src/com/cloud/capacity/CapacityManager.java @@ -102,4 +102,8 @@ public interface CapacityManager { boolean checkIfClusterCrossesThreshold(Long clusterId, Integer cpuRequested, long ramRequested); float getClusterOverProvisioningFactor(Long clusterId, short capacityType); + + long getUsedBytes(StoragePoolVO pool); + + long getUsedIops(StoragePoolVO pool); } diff --git a/plugins/storage/volume/cloudbyte/src/org/apache/cloudstack/storage/datastore/lifecycle/ElastistorPrimaryDataStoreLifeCycle.java b/plugins/storage/volume/cloudbyte/src/org/apache/cloudstack/storage/datastore/lifecycle/ElastistorPrimaryDataStoreLifeCycle.java index aa33b2f1d14..33004e3c698 100755 --- a/plugins/storage/volume/cloudbyte/src/org/apache/cloudstack/storage/datastore/lifecycle/ElastistorPrimaryDataStoreLifeCycle.java +++ b/plugins/storage/volume/cloudbyte/src/org/apache/cloudstack/storage/datastore/lifecycle/ElastistorPrimaryDataStoreLifeCycle.java @@ -493,5 +493,7 @@ public class ElastistorPrimaryDataStoreLifeCycle implements PrimaryDataStoreLife return false; } - + @Override + public void updateStoragePool(StoragePool storagePool, Map details) { + } } diff --git a/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImpl.java b/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImpl.java index 65236d96299..49eb8ef521f 100644 --- a/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImpl.java +++ b/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImpl.java @@ -525,4 +525,7 @@ public class CloudStackPrimaryDataStoreLifeCycleImpl implements PrimaryDataStore return false; } + @Override + public void updateStoragePool(StoragePool storagePool, Map details) { + } } diff --git a/plugins/storage/volume/nexenta/src/org/apache/cloudstack/storage/datastore/lifecylce/NexentaPrimaryDataStoreLifeCycle.java b/plugins/storage/volume/nexenta/src/org/apache/cloudstack/storage/datastore/lifecylce/NexentaPrimaryDataStoreLifeCycle.java index c7ce33ab054..493f2742e36 100644 --- a/plugins/storage/volume/nexenta/src/org/apache/cloudstack/storage/datastore/lifecylce/NexentaPrimaryDataStoreLifeCycle.java +++ b/plugins/storage/volume/nexenta/src/org/apache/cloudstack/storage/datastore/lifecylce/NexentaPrimaryDataStoreLifeCycle.java @@ -41,6 +41,7 @@ import com.cloud.host.HostVO; import com.cloud.hypervisor.Hypervisor; import com.cloud.resource.ResourceManager; import com.cloud.storage.StorageManager; +import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePoolAutomation; public class NexentaPrimaryDataStoreLifeCycle @@ -173,4 +174,8 @@ public class NexentaPrimaryDataStoreLifeCycle public boolean migrateToObjectStore(DataStore store) { return false; } + + @Override + public void updateStoragePool(StoragePool storagePool, Map details) { + } } diff --git a/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/lifecycle/SamplePrimaryDataStoreLifeCycleImpl.java b/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/lifecycle/SamplePrimaryDataStoreLifeCycleImpl.java index 6b5e43127b8..579cc2414e1 100644 --- a/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/lifecycle/SamplePrimaryDataStoreLifeCycleImpl.java +++ b/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/lifecycle/SamplePrimaryDataStoreLifeCycleImpl.java @@ -41,6 +41,7 @@ import com.cloud.agent.api.StoragePoolInfo; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePoolStatus; public class SamplePrimaryDataStoreLifeCycleImpl implements PrimaryDataStoreLifeCycle { @@ -135,4 +136,7 @@ public class SamplePrimaryDataStoreLifeCycleImpl implements PrimaryDataStoreLife return false; } + @Override + public void updateStoragePool(StoragePool storagePool, Map details) { + } } diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java index 7fc343622f3..c23db14a6a7 100644 --- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java @@ -33,15 +33,18 @@ import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreLifeCy import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreParameters; import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.util.SolidFireUtil; import org.apache.cloudstack.storage.volume.datastore.PrimaryDataStoreHelper; import com.cloud.agent.api.StoragePoolInfo; +import com.cloud.capacity.CapacityManager; import com.cloud.dc.DataCenterVO; import com.cloud.dc.dao.DataCenterDao; import com.cloud.host.HostVO; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.resource.ResourceManager; +import com.cloud.storage.StoragePool; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePoolAutomation; @@ -50,18 +53,13 @@ import com.cloud.utils.exception.CloudRuntimeException; public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeCycle { private static final Logger s_logger = Logger.getLogger(SolidFirePrimaryDataStoreLifeCycle.class); - @Inject - private DataCenterDao zoneDao; - @Inject - private PrimaryDataStoreDao storagePoolDao; - @Inject - private PrimaryDataStoreHelper dataStoreHelper; - @Inject - private ResourceManager _resourceMgr; - @Inject - private StorageManager _storageMgr; - @Inject - private StoragePoolAutomation storagePoolAutomation; + @Inject CapacityManager _capacityMgr; + @Inject private DataCenterDao zoneDao; + @Inject private PrimaryDataStoreDao storagePoolDao; + @Inject private PrimaryDataStoreHelper dataStoreHelper; + @Inject private ResourceManager _resourceMgr; + @Inject private StorageManager _storageMgr; + @Inject private StoragePoolAutomation storagePoolAutomation; // invoked to add primary storage that is based on the SolidFire plug-in @Override @@ -244,4 +242,30 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC return false; } + @Override + public void updateStoragePool(StoragePool storagePool, Map details) { + StoragePoolVO storagePoolVo = storagePoolDao.findById(storagePool.getId()); + + String strCapacityBytes = details.get(PrimaryDataStoreLifeCycle.CAPACITY_BYTES); + Long capacityBytes = strCapacityBytes != null ? Long.parseLong(strCapacityBytes) : null; + + if (capacityBytes != null) { + long usedBytes = _capacityMgr.getUsedBytes(storagePoolVo); + + if (capacityBytes < usedBytes) { + throw new CloudRuntimeException("Cannot reduce the number of bytes for this storage pool as it would lead to an insufficient number of bytes"); + } + } + + String strCapacityIops = details.get(PrimaryDataStoreLifeCycle.CAPACITY_IOPS); + Long capacityIops = strCapacityIops != null ? Long.parseLong(strCapacityIops) : null; + + if (capacityIops != null) { + long usedIops = _capacityMgr.getUsedIops(storagePoolVo); + + if (capacityIops < usedIops) { + throw new CloudRuntimeException("Cannot reduce the number of IOPS for this storage pool as it would lead to an insufficient number of IOPS"); + } + } + } } diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFireSharedPrimaryDataStoreLifeCycle.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFireSharedPrimaryDataStoreLifeCycle.java index ec2a52fc39e..b03634b8fcf 100644 --- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFireSharedPrimaryDataStoreLifeCycle.java +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFireSharedPrimaryDataStoreLifeCycle.java @@ -209,6 +209,10 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor throw new CloudRuntimeException("The parameter '" + CAPACITY_IOPS + "' must be equal to the parameter '" + SolidFireUtil.MIN_IOPS + "'."); } + if (lMinIops > SolidFireUtil.MAX_IOPS_PER_VOLUME || lMaxIops > SolidFireUtil.MAX_IOPS_PER_VOLUME || lBurstIops > SolidFireUtil.MAX_IOPS_PER_VOLUME) { + throw new CloudRuntimeException("This volume cannot exceed " + NumberFormat.getInstance().format(SolidFireUtil.MAX_IOPS_PER_VOLUME) + " IOPS."); + } + details.put(SolidFireUtil.MIN_IOPS, String.valueOf(lMinIops)); details.put(SolidFireUtil.MAX_IOPS, String.valueOf(lMaxIops)); details.put(SolidFireUtil.BURST_IOPS, String.valueOf(lBurstIops)); @@ -302,7 +306,7 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor } long sfVolumeId = SolidFireUtil.createSolidFireVolume(sfConnection, SolidFireUtil.getSolidFireVolumeName(volumeName), sfAccount.getId(), volumeSize, - true, NumberFormat.getInstance().format(volumeSize), minIops, maxIops, burstIops); + true, null, minIops, maxIops, burstIops); SolidFireUtil.SolidFireVolume sfVolume = SolidFireUtil.getSolidFireVolume(sfConnection, sfVolumeId); return sfVolume; @@ -523,6 +527,14 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor return Long.parseLong(volumeId); } + private long getIopsValue(long storagePoolId, String iopsKey) { + StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, iopsKey); + + String iops = storagePoolDetail.getValue(); + + return Long.parseLong(iops); + } + private static boolean isSupportedHypervisorType(HypervisorType hypervisorType) { return HypervisorType.XenServer.equals(hypervisorType) || HypervisorType.VMware.equals(hypervisorType); } @@ -545,4 +557,49 @@ public class SolidFireSharedPrimaryDataStoreLifeCycle implements PrimaryDataStor return false; } + @Override + public void updateStoragePool(StoragePool storagePool, Map details) { + String strCapacityBytes = details.get(PrimaryDataStoreLifeCycle.CAPACITY_BYTES); + String strCapacityIops = details.get(PrimaryDataStoreLifeCycle.CAPACITY_IOPS); + + Long capacityBytes = strCapacityBytes != null ? Long.parseLong(strCapacityBytes) : null; + Long capacityIops = strCapacityIops != null ? Long.parseLong(strCapacityIops) : null; + + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePool.getId(), _storagePoolDetailsDao); + + long size = capacityBytes != null ? capacityBytes : storagePool.getCapacityBytes(); + + long currentMinIops = getIopsValue(storagePool.getId(), SolidFireUtil.MIN_IOPS); + long currentMaxIops = getIopsValue(storagePool.getId(), SolidFireUtil.MAX_IOPS); + long currentBurstIops = getIopsValue(storagePool.getId(), SolidFireUtil.BURST_IOPS); + + long minIops = currentMinIops; + long maxIops = currentMaxIops; + long burstIops = currentBurstIops; + + if (capacityIops != null) { + if (capacityIops > SolidFireUtil.MAX_IOPS_PER_VOLUME) { + throw new CloudRuntimeException("This volume cannot exceed " + NumberFormat.getInstance().format(SolidFireUtil.MAX_IOPS_PER_VOLUME) + " IOPS."); + } + + float maxPercentOfMin = currentMaxIops / (float)currentMinIops; + float burstPercentOfMax = currentBurstIops / (float)currentMaxIops; + + minIops = capacityIops; + maxIops = (long)(minIops * maxPercentOfMin); + burstIops = (long)(maxIops * burstPercentOfMax); + + if (maxIops > SolidFireUtil.MAX_IOPS_PER_VOLUME) { + maxIops = SolidFireUtil.MAX_IOPS_PER_VOLUME; + } + + if (burstIops > SolidFireUtil.MAX_IOPS_PER_VOLUME) { + burstIops = SolidFireUtil.MAX_IOPS_PER_VOLUME; + } + } + + SolidFireUtil.modifySolidFireVolume(sfConnection, getVolumeId(storagePool.getId()), size, minIops, maxIops, burstIops); + + SolidFireUtil.updateCsDbWithSolidFireIopsInfo(storagePool.getId(), _primaryDataStoreDao, _storagePoolDetailsDao, minIops, maxIops, burstIops); + } } diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java index a27917b1c8c..3f35d71e207 100644 --- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java @@ -28,7 +28,10 @@ import java.security.SecureRandom; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.StringTokenizer; import java.util.UUID; @@ -36,6 +39,7 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; import org.apache.commons.lang.StringUtils; @@ -99,6 +103,8 @@ public class SolidFireUtil { public static final String DATASTORE_NAME = "datastoreName"; public static final String IQN = "iqn"; + public static final long MAX_IOPS_PER_VOLUME = 100000; + private static final int DEFAULT_MANAGEMENT_PORT = 443; private static final int DEFAULT_STORAGE_PORT = 3260; @@ -156,6 +162,30 @@ public class SolidFireUtil { return "CloudStack_" + csAccountUuid + "_" + csAccountId; } + public static void updateCsDbWithSolidFireIopsInfo(long storagePoolId, PrimaryDataStoreDao primaryDataStoreDao, StoragePoolDetailsDao storagePoolDetailsDao, + long minIops, long maxIops, long burstIops) { + Map existingDetails = storagePoolDetailsDao.listDetailsKeyPairs(storagePoolId); + Set existingKeys = existingDetails.keySet(); + + Map existingDetailsToKeep = new HashMap(); + + for (String existingKey : existingKeys) { + String existingValue = existingDetails.get(existingKey); + + if (!SolidFireUtil.MIN_IOPS.equalsIgnoreCase(existingValue) && + !SolidFireUtil.MAX_IOPS.equalsIgnoreCase(existingValue) && + !SolidFireUtil.BURST_IOPS.equalsIgnoreCase(existingValue)) { + existingDetailsToKeep.put(existingKey, existingValue); + } + } + + existingDetailsToKeep.put(SolidFireUtil.MIN_IOPS, String.valueOf(minIops)); + existingDetailsToKeep.put(SolidFireUtil.MAX_IOPS, String.valueOf(maxIops)); + existingDetailsToKeep.put(SolidFireUtil.BURST_IOPS, String.valueOf(burstIops)); + + primaryDataStoreDao.updateDetails(storagePoolId, existingDetailsToKeep); + } + public static void updateCsDbWithSolidFireAccountInfo(long csAccountId, SolidFireUtil.SolidFireAccount sfAccount, AccountDetailsDao accountDetailsDao) { AccountDetailVO accountDetail = new AccountDetailVO(csAccountId, @@ -402,11 +432,13 @@ public class SolidFireUtil { } public static long createSolidFireVolume(SolidFireConnection sfConnection, String strSfVolumeName, long lSfAccountId, long lTotalSize, - boolean bEnable512e, final String strCloudStackVolumeSize, long lMinIops, long lMaxIops, long lBurstIops) + boolean bEnable512e, String strCloudStackVolumeSize, long minIops, long maxIops, long burstIops) { final Gson gson = new GsonBuilder().create(); - VolumeToCreate volumeToCreate = new VolumeToCreate(strSfVolumeName, lSfAccountId, lTotalSize, bEnable512e, strCloudStackVolumeSize, lMinIops, lMaxIops, lBurstIops); + Object volumeToCreate = strCloudStackVolumeSize != null && strCloudStackVolumeSize.trim().length() > 0 ? + new VolumeToCreateWithCloudStackVolumeSize(strSfVolumeName, lSfAccountId, lTotalSize, bEnable512e, strCloudStackVolumeSize, minIops, maxIops, burstIops) : + new VolumeToCreate(strSfVolumeName, lSfAccountId, lTotalSize, bEnable512e, minIops, maxIops, burstIops); String strVolumeToCreateJson = gson.toJson(volumeToCreate); @@ -419,6 +451,23 @@ public class SolidFireUtil { return volumeCreateResult.result.volumeID; } + public static void modifySolidFireVolume(SolidFireConnection sfConnection, long volumeId, long totalSize, long minIops, long maxIops, long burstIops) + { + final Gson gson = new GsonBuilder().create(); + + VolumeToModify volumeToModify = new VolumeToModify(volumeId, totalSize, minIops, maxIops, burstIops); + + String strVolumeToModifyJson = gson.toJson(volumeToModify); + + String strVolumeModifyResultJson = executeJsonRpc(sfConnection, strVolumeToModifyJson); + + JsonError jsonError = gson.fromJson(strVolumeModifyResultJson, JsonError.class); + + if (jsonError.error != null) { + throw new IllegalStateException(jsonError.error.message); + } + } + public static SolidFireVolume getSolidFireVolume(SolidFireConnection sfConnection, long lVolumeId) { final Gson gson = new GsonBuilder().create(); @@ -868,12 +917,12 @@ public class SolidFireUtil { } @SuppressWarnings("unused") - private static final class VolumeToCreate { + private static final class VolumeToCreateWithCloudStackVolumeSize { private final String method = "CreateVolume"; private final VolumeToCreateParams params; - private VolumeToCreate(final String strVolumeName, final long lAccountId, final long lTotalSize, final boolean bEnable512e, final String strCloudStackVolumeSize, - final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS) { + private VolumeToCreateWithCloudStackVolumeSize(final String strVolumeName, final long lAccountId, final long lTotalSize, + final boolean bEnable512e, final String strCloudStackVolumeSize, final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS) { params = new VolumeToCreateParams(strVolumeName, lAccountId, lTotalSize, bEnable512e, strCloudStackVolumeSize, lMinIOPS, lMaxIOPS, lBurstIOPS); } @@ -918,6 +967,87 @@ public class SolidFireUtil { } } + @SuppressWarnings("unused") + private static final class VolumeToCreate { + private final String method = "CreateVolume"; + private final VolumeToCreateParams params; + + private VolumeToCreate(final String strVolumeName, final long lAccountId, final long lTotalSize, final boolean bEnable512e, + final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS) { + params = new VolumeToCreateParams(strVolumeName, lAccountId, lTotalSize, bEnable512e, lMinIOPS, lMaxIOPS, lBurstIOPS); + } + + private static final class VolumeToCreateParams { + private final String name; + private final long accountID; + private final long totalSize; + private final boolean enable512e; + private final VolumeToCreateParamsQoS qos; + + private VolumeToCreateParams(final String strVolumeName, final long lAccountId, final long lTotalSize, final boolean bEnable512e, + final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS) { + name = strVolumeName; + accountID = lAccountId; + totalSize = lTotalSize; + enable512e = bEnable512e; + + qos = new VolumeToCreateParamsQoS(lMinIOPS, lMaxIOPS, lBurstIOPS); + } + + private static final class VolumeToCreateParamsQoS { + private final long minIOPS; + private final long maxIOPS; + private final long burstIOPS; + + private VolumeToCreateParamsQoS(final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS) { + minIOPS = lMinIOPS; + maxIOPS = lMaxIOPS; + burstIOPS = lBurstIOPS; + } + } + } + } + + @SuppressWarnings("unused") + private static final class VolumeToModify + { + private final String method = "ModifyVolume"; + private final VolumeToModifyParams params; + + private VolumeToModify(final long lVolumeId, final long lTotalSize, final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS) + { + params = new VolumeToModifyParams(lVolumeId, lTotalSize, lMinIOPS, lMaxIOPS, lBurstIOPS); + } + + private static final class VolumeToModifyParams + { + private final long volumeID; + private final long totalSize; + private final VolumeToModifyParamsQoS qos; + + private VolumeToModifyParams(final long lVolumeId, final long lTotalSize, final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS) + { + volumeID = lVolumeId; + + totalSize = lTotalSize; + + qos = new VolumeToModifyParamsQoS(lMinIOPS, lMaxIOPS, lBurstIOPS); + } + } + + private static final class VolumeToModifyParamsQoS { + private final long minIOPS; + private final long maxIOPS; + private final long burstIOPS; + + private VolumeToModifyParamsQoS(final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS) { + minIOPS = lMinIOPS; + maxIOPS = lMaxIOPS; + burstIOPS = lBurstIOPS; + } + } + } + @SuppressWarnings("unused") private static final class VolumeToGet { @@ -1376,8 +1506,8 @@ public class SolidFireUtil { return iCode >= 200 && iCode < 300; } - private static void verifyResult(Object obj, String strJson, Gson gson) throws IllegalStateException { - if (obj != null) { + private static void verifyResult(Object result, String strJson, Gson gson) throws IllegalStateException { + if (result != null) { return; } diff --git a/server/src/com/cloud/capacity/CapacityManagerImpl.java b/server/src/com/cloud/capacity/CapacityManagerImpl.java index 14fbe7f80b6..e57604e9f10 100755 --- a/server/src/com/cloud/capacity/CapacityManagerImpl.java +++ b/server/src/com/cloud/capacity/CapacityManagerImpl.java @@ -514,7 +514,8 @@ public class CapacityManagerImpl extends ManagerBase implements CapacityManager, } - private long getUsedBytes(StoragePoolVO pool) { + @Override + public long getUsedBytes(StoragePoolVO pool) { long usedBytes = 0; List volumes = _volumeDao.findByPoolId(pool.getId(), null); @@ -541,6 +542,21 @@ public class CapacityManagerImpl extends ManagerBase implements CapacityManager, return usedBytes; } + @Override + public long getUsedIops(StoragePoolVO pool) { + long usedIops = 0; + + List volumes = _volumeDao.findByPoolId(pool.getId(), null); + + if (volumes != null && volumes.size() > 0) { + for (VolumeVO volume : volumes) { + usedIops += volume.getMinIops(); + } + } + + return usedIops; + } + @Override public long getAllocatedPoolCapacity(StoragePoolVO pool, VMTemplateVO templateForVmCreation) { long totalAllocatedSize = 0; diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java index de4ad4b2c44..07ae6958db9 100755 --- a/server/src/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/com/cloud/storage/StorageManagerImpl.java @@ -67,6 +67,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener import org.apache.cloudstack.engine.subsystem.api.storage.ImageStoreProvider; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreLifeCycle; 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.TemplateDataFactory; @@ -755,10 +756,8 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C Long capacityBytes = cmd.getCapacityBytes(); if (capacityBytes != null) { - if (capacityBytes > pool.getCapacityBytes()) { + if (capacityBytes != pool.getCapacityBytes()) { updatedCapacityBytes = capacityBytes; - } else if (capacityBytes < pool.getCapacityBytes()) { - throw new CloudRuntimeException("The value of 'Capacity bytes' cannot be reduced in this version."); } } @@ -766,10 +765,23 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C Long capacityIops = cmd.getCapacityIops(); if (capacityIops != null) { - if (capacityIops > pool.getCapacityIops()) { + if (capacityIops != pool.getCapacityIops()) { updatedCapacityIops = capacityIops; - } else if (capacityIops < pool.getCapacityIops()) { - throw new CloudRuntimeException("The value of 'Capacity IOPS' cannot be reduced in this version."); + } + } + + if (updatedCapacityBytes != null || updatedCapacityIops != null) { + StoragePoolVO storagePool = _storagePoolDao.findById(id); + DataStoreProvider dataStoreProvider = dataStoreProviderMgr.getDataStoreProvider(storagePool.getStorageProviderName()); + DataStoreLifeCycle dataStoreLifeCycle = dataStoreProvider.getDataStoreLifeCycle(); + + if (dataStoreLifeCycle instanceof PrimaryDataStoreLifeCycle) { + Map details = new HashMap(); + + details.put(PrimaryDataStoreLifeCycle.CAPACITY_BYTES, updatedCapacityBytes != null ? String.valueOf(updatedCapacityBytes) : null); + details.put(PrimaryDataStoreLifeCycle.CAPACITY_IOPS, updatedCapacityIops != null ? String.valueOf(updatedCapacityIops) : null); + + ((PrimaryDataStoreLifeCycle)dataStoreLifeCycle).updateStoragePool(storagePool, details); } } @@ -1507,20 +1519,13 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C // Only IOPS guaranteed primary storage like SolidFire is using/setting IOPS. // This check returns true for storage that does not specify IOPS. if (pool.getCapacityIops() == null ) { - s_logger.info("Storage pool " + pool.getName() + " (" + pool.getId() + ") does not supply Iops capacity, assuming enough capacity"); + s_logger.info("Storage pool " + pool.getName() + " (" + pool.getId() + ") does not supply IOPS capacity, assuming enough capacity"); + return true; } - long currentIops = 0; - List volumesInPool = _volumeDao.findByPoolId(pool.getId(), null); - - for (VolumeVO volumeInPool : volumesInPool) { - Long minIops = volumeInPool.getMinIops(); - - if (minIops != null && minIops > 0) { - currentIops += minIops; - } - } + StoragePoolVO storagePoolVo = _storagePoolDao.findById(pool.getId()); + long currentIops = _capacityMgr.getUsedIops(storagePoolVo); long requestedIops = 0; diff --git a/ui/scripts/sharedFunctions.js b/ui/scripts/sharedFunctions.js index f58fdd21768..e8c4fcb7edc 100644 --- a/ui/scripts/sharedFunctions.js +++ b/ui/scripts/sharedFunctions.js @@ -900,6 +900,40 @@ cloudStack.converters = { return (bytes / 1024 / 1024 / 1024 / 1024).toFixed(2) + " TB"; } }, + toBytes: function(str) { + if (str === undefined) { + return "0"; + } + + var res = str.split(" "); + + if (res.length === 1) { + // assume a number in GB + + return parseInt(str, 10) * 1024 * 1024 * 1024; + } + + // assume first string is a number and second string is a unit of size + + if (res[1] === "KB") { + return parseInt(res[0], 10) * 1024; + } + + if (res[1] === "MB") { + return parseInt(res[0], 10) * 1024 * 1024; + } + + if (res[1] === "GB") { + return parseInt(res[0], 10) * 1024 * 1024 * 1024; + } + + if (res[1] === "TB") { + return parseInt(res[0], 10) * 1024 * 1024 * 1024 * 1024; + } + + // assume GB + return parseInt(res[0], 10) * 1024 * 1024 * 1024; + }, toLocalDate: function(UtcDate) { var localDate = ""; if (UtcDate != null && UtcDate.length > 0) { diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 7d49d8d7250..9a98a5ca432 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -16113,6 +16113,14 @@ var array1 =[]; array1.push("&tags=" + todb(args.data.tags)); + if (args.data.disksizetotal != null && args.data.disksizetotal.length > 0) { + array1.push("&capacitybytes=" + cloudStack.converters.toBytes(args.data.disksizetotal)); + } + + if (args.data.capacityiops != null && args.data.capacityiops.length > 0) { + array1.push("&capacityiops=" + args.data.capacityiops); + } + $.ajax({ url: createURL("updateStoragePool&id=" + args.context.primarystorages[0].id + array1.join("")), dataType: "json", @@ -16279,6 +16287,7 @@ }, disksizetotal: { label: 'label.disk.total', + isEditable: true, converter: function (args) { if (args == null || args == 0) return ""; else @@ -16295,6 +16304,7 @@ }, capacityiops: { label: 'label.disk.iops.total', + isEditable: true, converter: function (args) { if (args == null || args == 0) return ""; else