From c2a3410d5c42b1c815df2df46a00c93c2464910f Mon Sep 17 00:00:00 2001 From: Abhinandan Prateek Date: Mon, 2 Jan 2017 14:38:05 +0530 Subject: [PATCH 1/2] CLOUDSTACK-9473: storage pool capacity check when volume is resized or migrated Storage pool checker is not being called on resize and migrate volume. This may lead to allocated percentage of storage above 100%. Setup: 1 VMware cluster with 2 Hosts. Executed Steps: Applied the following global settings: storage.overprovisioning.factor = 1 pool.storage.allocated.capacity.disablethreshold = 1 pool.storage.capacity.disablethreshold = 1 Restarted management server Executed Resize and migrate pool and Observed that Storage pool checker is not performed on resizeVolume and migrateVolume. Result: Root cause analysis shows storage pool checker is not called when doing migration and resizing. Signed-off-by: Rohit Yadav --- .../src/com/cloud/storage/StorageManager.java | 2 + .../com/cloud/storage/StorageManagerImpl.java | 50 +++++++++++++++---- .../cloud/storage/VolumeApiServiceImpl.java | 16 ++++++ 3 files changed, 58 insertions(+), 10 deletions(-) diff --git a/engine/components-api/src/com/cloud/storage/StorageManager.java b/engine/components-api/src/com/cloud/storage/StorageManager.java index 530a7dea3ccf..a532a4f41e77 100644 --- a/engine/components-api/src/com/cloud/storage/StorageManager.java +++ b/engine/components-api/src/com/cloud/storage/StorageManager.java @@ -178,6 +178,8 @@ public interface StorageManager extends StorageService { */ boolean storagePoolHasEnoughSpace(List volume, StoragePool pool, Long clusterId); + boolean storagePoolHasEnoughSpaceForResize(StoragePool pool, long currentSize, long newSiz); + boolean registerHostListener(String providerUuid, HypervisorHostListener listener); void connectHostToSharedPool(long hostId, long poolId) throws StorageUnavailableException, StorageConflictException; diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java index 610789f5de26..44c8189f6e10 100644 --- a/server/src/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/com/cloud/storage/StorageManagerImpl.java @@ -1788,8 +1788,8 @@ public boolean storagePoolHasEnoughSpace(List volumes, StoragePool pool, if (s_logger.isDebugEnabled()) { s_logger.debug("Destination pool id: " + pool.getId()); } - - StoragePoolVO poolVO = _storagePoolDao.findById(pool.getId()); + // allocated space includes templates + final StoragePoolVO poolVO = _storagePoolDao.findById(pool.getId()); long allocatedSizeWithTemplate = _capacityMgr.getAllocatedPoolCapacity(poolVO, null); long totalAskingSize = 0; @@ -1830,12 +1830,37 @@ public boolean storagePoolHasEnoughSpace(List volumes, StoragePool pool, } } + return checkPoolforSpace(pool, allocatedSizeWithTemplate, totalAskingSize); + } + + @Override + public boolean storagePoolHasEnoughSpaceForResize(StoragePool pool, long currentSize, long newSiz) { + if (!checkUsagedSpace(pool)) { + return false; + } + if (s_logger.isDebugEnabled()) { + s_logger.debug("Destination pool id: " + pool.getId()); + } + long totalAskingSize = newSiz - currentSize; + + if (totalAskingSize <= 0) { + return true; + } else { + final StoragePoolVO poolVO = _storagePoolDao.findById(pool.getId()); + final long allocatedSizeWithTemplate = _capacityMgr.getAllocatedPoolCapacity(poolVO, null); + return checkPoolforSpace(pool, allocatedSizeWithTemplate, totalAskingSize); + } + } + + private boolean checkPoolforSpace(StoragePool pool, long allocatedSizeWithTemplate, long totalAskingSize) { + // allocated space includes templates + StoragePoolVO poolVO = _storagePoolDao.findById(pool.getId()); + long totalOverProvCapacity; if (pool.getPoolType().supportsOverProvisioning()) { BigDecimal overProvFactor = getStorageOverProvisioningFactor(pool.getId()); totalOverProvCapacity = overProvFactor.multiply(new BigDecimal(pool.getCapacityBytes())).longValue(); - s_logger.debug("Found storage pool " + poolVO.getName() + " of type " + pool.getPoolType().toString() + " with overprovisioning factor " + overProvFactor.toString()); s_logger.debug("Total over provisioned capacity calculated is " + overProvFactor + " * " + pool.getCapacityBytes()); } else { @@ -1847,21 +1872,26 @@ public boolean storagePoolHasEnoughSpace(List volumes, StoragePool pool, s_logger.debug("Total capacity of the pool " + poolVO.getName() + " with ID " + pool.getId() + " is " + totalOverProvCapacity); double storageAllocatedThreshold = CapacityManager.StorageAllocatedCapacityDisableThreshold.valueIn(pool.getDataCenterId()); - - s_logger.debug("Checking pool: " + pool.getId() + " for volume allocation " + volumes.toString() + ", maxSize : " + totalOverProvCapacity + ", totalAllocatedSize : " - + allocatedSizeWithTemplate + ", askingSize : " + totalAskingSize + ", allocated disable threshold: " + storageAllocatedThreshold); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Checking pool: " + pool.getId() + " for storage allocation , maxSize : " + totalOverProvCapacity + ", totalAllocatedSize : " + allocatedSizeWithTemplate + + ", askingSize : " + totalAskingSize + ", allocated disable threshold: " + storageAllocatedThreshold); + } double usedPercentage = (allocatedSizeWithTemplate + totalAskingSize) / (double)(totalOverProvCapacity); if (usedPercentage > storageAllocatedThreshold) { - s_logger.debug("Insufficient un-allocated capacity on: " + pool.getId() + " for volume allocation: " + volumes.toString() + " since its allocated percentage: " + usedPercentage - + " has crossed the allocated pool.storage.allocated.capacity.disablethreshold: " + storageAllocatedThreshold + ", skipping this pool"); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Insufficient un-allocated capacity on: " + pool.getId() + " for storage allocation since its allocated percentage: " + usedPercentage + + " has crossed the allocated pool.storage.allocated.capacity.disablethreshold: " + storageAllocatedThreshold + ", skipping this pool"); + } return false; } if (totalOverProvCapacity < (allocatedSizeWithTemplate + totalAskingSize)) { - s_logger.debug("Insufficient un-allocated capacity on: " + pool.getId() + " for volume allocation: " + volumes.toString() + ", not enough storage, maxSize : " + totalOverProvCapacity - + ", totalAllocatedSize : " + allocatedSizeWithTemplate + ", askingSize : " + totalAskingSize); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Insufficient un-allocated capacity on: " + pool.getId() + " for storage allocation, not enough storage, maxSize : " + totalOverProvCapacity + + ", totalAllocatedSize : " + allocatedSizeWithTemplate + ", askingSize : " + totalAskingSize); + } return false; } diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java index 5e3bf54da19a..1bf32973e4a0 100644 --- a/server/src/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/com/cloud/storage/VolumeApiServiceImpl.java @@ -1137,6 +1137,14 @@ private VolumeVO orchestrateResizeVolume(long volumeId, long currentSize, long n UserVmVO userVm = _userVmDao.findById(volume.getInstanceId()); StoragePoolVO storagePool = _storagePoolDao.findById(volume.getPoolId()); boolean isManaged = storagePool.isManaged(); + + List volumes = new ArrayList(); + volumes.add(volume); + + //check if there is space + if (!storageMgr.storagePoolHasEnoughSpaceForResize(storagePool, currentSize, newSize)) { + throw new CloudRuntimeException("Storage pool " + storagePool.getName() + " does not have enough space to resize volume " + volume.getName()); + } /* * get a list of hosts to send the commands to, try the system the * associated vm is running on first, then the last known place it ran. @@ -2061,6 +2069,14 @@ public Volume migrateVolume(MigrateVolumeCmd cmd) { throw new InvalidParameterValueException("Cannot migrate volume " + vol + "to the destination storage pool " + destPool.getName() + " as the storage pool is in maintenance mode."); } + List volumes = new ArrayList(); + volumes.add(vol); + + //check if there is space + if (!storageMgr.storagePoolHasEnoughSpace(volumes, destPool)) { + throw new CloudRuntimeException("Storage pool " + destPool.getName() + " does not have enough space to migrate volume " + vol.getName()); + } + if (_volumeMgr.volumeOnSharedStoragePool(vol)) { if (destPool.isLocal()) { throw new InvalidParameterValueException("Migration of volume from shared to local storage pool is not supported"); From 39214209e2cd66b63278792af04a2ca4bbe737cb Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Tue, 4 Sep 2018 13:25:50 +0530 Subject: [PATCH 2/2] fix review comments Signed-off-by: Rohit Yadav --- .../src/com/cloud/storage/VolumeApiServiceImpl.java | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java index 1bf32973e4a0..372ab3423af4 100644 --- a/server/src/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/com/cloud/storage/VolumeApiServiceImpl.java @@ -19,6 +19,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -1138,10 +1139,6 @@ private VolumeVO orchestrateResizeVolume(long volumeId, long currentSize, long n StoragePoolVO storagePool = _storagePoolDao.findById(volume.getPoolId()); boolean isManaged = storagePool.isManaged(); - List volumes = new ArrayList(); - volumes.add(volume); - - //check if there is space if (!storageMgr.storagePoolHasEnoughSpaceForResize(storagePool, currentSize, newSize)) { throw new CloudRuntimeException("Storage pool " + storagePool.getName() + " does not have enough space to resize volume " + volume.getName()); } @@ -2069,11 +2066,7 @@ public Volume migrateVolume(MigrateVolumeCmd cmd) { throw new InvalidParameterValueException("Cannot migrate volume " + vol + "to the destination storage pool " + destPool.getName() + " as the storage pool is in maintenance mode."); } - List volumes = new ArrayList(); - volumes.add(vol); - - //check if there is space - if (!storageMgr.storagePoolHasEnoughSpace(volumes, destPool)) { + if (!storageMgr.storagePoolHasEnoughSpace(Collections.singletonList(vol), destPool)) { throw new CloudRuntimeException("Storage pool " + destPool.getName() + " does not have enough space to migrate volume " + vol.getName()); }