diff --git a/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java b/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java index 3d6d0626c55..7976b313167 100644 --- a/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java +++ b/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java @@ -149,6 +149,9 @@ public interface StorageManager extends StorageService { "If set to true, the disk is created only when there is a suitable storage pool that supports the disk provisioning type specified by the service/disk offering. " + "If set to false, the disk is created with a disk provisioning type supported by the pool. Default value is false, and this is currently supported for VMware only.", true, ConfigKey.Scope.Zone); + ConfigKey PreferredStoragePool = new ConfigKey(String.class, "preferred.storage.pool", "Advanced", "", + "The UUID of preferred storage pool for allocation.", true, ConfigKey.Scope.Account, null); + /** * Returns a comma separated list of tags for the specified storage pool * @param poolId diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java index 5a42b3a6b93..9322481f211 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java @@ -26,6 +26,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutionException; @@ -300,6 +301,31 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati return _volsDao.persist(newVol); } + private Optional getMatchingStoragePool(String preferredPoolId, List storagePools) { + if (preferredPoolId == null) { + return Optional.empty(); + } + return storagePools.stream() + .filter(pool -> pool.getUuid().equalsIgnoreCase(preferredPoolId)) + .findFirst(); + } + + private Optional getPreferredStoragePool(List poolList, VirtualMachine vm) { + String accountStoragePoolUuid = StorageManager.PreferredStoragePool.valueIn(vm.getAccountId()); + Optional storagePool = getMatchingStoragePool(accountStoragePoolUuid, poolList); + + if (storagePool.isPresent()) { + s_logger.debug("A storage pool is specified for this account, so we will use this storage pool for allocation: " + + storagePool.get().getUuid()); + } else { + String globalStoragePoolUuid = StorageManager.PreferredStoragePool.value(); + storagePool = getMatchingStoragePool(globalStoragePoolUuid, poolList); + storagePool.ifPresent(pool -> s_logger.debug("A storage pool is specified in global setting, so we will use this storage pool for allocation: " + + pool.getUuid())); + } + return storagePool; + } + @Override public StoragePool findStoragePool(DiskProfile dskCh, DataCenter dc, Pod pod, Long clusterId, Long hostId, VirtualMachine vm, final Set avoid) { Long podId = null; @@ -321,9 +347,13 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati } DataCenterDeployment plan = new DataCenterDeployment(dc.getId(), podId, clusterId, hostId, null, null); - final List poolList = allocator.allocateToPool(dskCh, profile, plan, avoidList, 1); + final List poolList = allocator.allocateToPool(dskCh, profile, plan, avoidList, StoragePoolAllocator.RETURN_UPTO_ALL); if (poolList != null && !poolList.isEmpty()) { - return (StoragePool)dataStoreMgr.getDataStore(poolList.get(0).getId(), DataStoreRole.Primary); + // Check if the preferred storage pool can be used. If yes, use it. + Optional storagePool = getPreferredStoragePool(poolList, vm); + + return (storagePool.isPresent()) ? (StoragePool) this.dataStoreMgr.getDataStore(storagePool.get().getId(), DataStoreRole.Primary) : + (StoragePool)dataStoreMgr.getDataStore(poolList.get(0).getId(), DataStoreRole.Primary); } } return null; diff --git a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java index afcaacf0143..a225015ebff 100644 --- a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.Timer; import java.util.TreeSet; @@ -1674,7 +1675,7 @@ StateListener, Configurable { for (StoragePoolAllocator allocator : _storagePoolAllocators) { final List suitablePools = allocator.allocateToPool(diskProfile, vmProfile, plan, avoid, returnUpTo); if (suitablePools != null && !suitablePools.isEmpty()) { - suitableVolumeStoragePools.put(toBeCreated, suitablePools); + checkForPreferredStoragePool(suitablePools, vmProfile.getVirtualMachine(), suitableVolumeStoragePools, toBeCreated); foundPotentialPools = true; break; } @@ -1715,6 +1716,43 @@ StateListener, Configurable { return new Pair>, List>(suitableVolumeStoragePools, readyAndReusedVolumes); } + private void checkForPreferredStoragePool(List suitablePools, + VirtualMachine vm, + Map> suitableVolumeStoragePools, + VolumeVO toBeCreated) { + List pools = new ArrayList<>(); + Optional storagePool = getPreferredStoragePool(suitablePools, vm); + storagePool.ifPresent(pools::add); + + pools.addAll(suitablePools); + suitableVolumeStoragePools.put(toBeCreated, pools); + } + + private Optional getMatchingStoragePool(String preferredPoolId, List storagePools) { + if (preferredPoolId == null) { + return Optional.empty(); + } + return storagePools.stream() + .filter(pool -> pool.getUuid().equalsIgnoreCase(preferredPoolId)) + .findFirst(); + } + + private Optional getPreferredStoragePool(List poolList, VirtualMachine vm) { + String accountStoragePoolUuid = StorageManager.PreferredStoragePool.valueIn(vm.getAccountId()); + Optional storagePool = getMatchingStoragePool(accountStoragePoolUuid, poolList); + + if (storagePool.isPresent()) { + s_logger.debug("A storage pool is specified for this account, so we will use this storage pool for allocation: " + + storagePool.get().getUuid()); + } else { + String globalStoragePoolUuid = StorageManager.PreferredStoragePool.value(); + storagePool = getMatchingStoragePool(globalStoragePoolUuid, poolList); + storagePool.ifPresent(pool -> s_logger.debug("A storage pool is specified in global setting, so we will use this storage pool for allocation: " + + pool.getUuid())); + } + return storagePool; + } + private boolean isEnabledForAllocation(long zoneId, Long podId, Long clusterId) { // Check if the zone exists in the system DataCenterVO zone = _dcDao.findById(zoneId); diff --git a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java index d75f8335787..01932169a38 100644 --- a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java @@ -3212,7 +3212,8 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C PRIMARY_STORAGE_DOWNLOAD_WAIT, SecStorageMaxMigrateSessions, MaxDataMigrationWaitTime, - DiskProvisioningStrictness + DiskProvisioningStrictness, + PreferredStoragePool }; }