From dc5ba75f6ca26a00b4c4266f5796ad8d8286a906 Mon Sep 17 00:00:00 2001 From: Pearl Dsilva Date: Fri, 16 Jan 2026 08:28:04 -0500 Subject: [PATCH 1/3] Consider flexible tags as for detached volume migration --- .../cloud/server/ManagementServerImpl.java | 58 ++++++++++++++----- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index caf94a1cc854..bdf539bdd8b0 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -25,11 +25,11 @@ import java.util.Comparator; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.Set; import java.util.TimeZone; import java.util.UUID; @@ -663,7 +663,6 @@ import com.cloud.alert.dao.AlertDao; import com.cloud.api.ApiDBUtils; import com.cloud.api.query.dao.StoragePoolJoinDao; -import com.cloud.api.query.vo.StoragePoolJoinVO; import com.cloud.capacity.Capacity; import com.cloud.capacity.CapacityVO; import com.cloud.capacity.dao.CapacityDao; @@ -768,7 +767,6 @@ import com.cloud.storage.Storage; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; -import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.Volume; import com.cloud.storage.VolumeApiServiceImpl; @@ -1991,23 +1989,51 @@ private List findAllSuitableStoragePoolsForVm(final VolumeVO volume private List findAllSuitableStoragePoolsForDetachedVolume(Volume volume, Long diskOfferingId, List allPools) { List suitablePools = new ArrayList<>(); if (CollectionUtils.isEmpty(allPools)) { - return suitablePools; + return suitablePools; } + + StoragePoolVO srcPool = _poolDao.findById(volume.getPoolId()); + if (srcPool == null) { + logger.warn("Source pool not found for volume {}: {}", volume.getName(), volume.getUuid()); + return suitablePools; + } + + HypervisorType hypervisorType = getHypervisorType(null, srcPool); + List clusters = _clusterDao.listByDcHyType(srcPool.getDataCenterId(), hypervisorType.toString()); + DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId); - List tags = new ArrayList<>(); - String[] tagsArray = diskOffering.getTagsArray(); - if (tagsArray != null && tagsArray.length > 0) { - tags = Arrays.asList(tagsArray); - } - Long[] poolIds = allPools.stream().map(StoragePool::getId).toArray(Long[]::new); - List pools = _poolJoinDao.searchByIds(poolIds); - for (StoragePoolJoinVO storagePool : pools) { - if (StoragePoolStatus.Up.equals(storagePool.getStatus()) && - (CollectionUtils.isEmpty(tags) || tags.contains(storagePool.getTag()))) { - Optional match = allPools.stream().filter(x -> x.getId() == storagePool.getId()).findFirst(); - match.ifPresent(suitablePools::add); + DiskProfile diskProfile = new DiskProfile(volume, diskOffering, hypervisorType); + + ExcludeList avoid = new ExcludeList(); + + Set allPoolIds = allPools.stream() + .map(StoragePool::getId) + .collect(Collectors.toSet()); + Set addedPoolIds = new HashSet<>(); + + for (Cluster cluster : clusters) { + DataCenterDeployment plan = new DataCenterDeployment(srcPool.getDataCenterId(), cluster.getPodId(), cluster.getId(), + null, null, null, null); + + for (StoragePoolAllocator allocator : _storagePoolAllocators) { + try { + List pools = allocator.allocateToPool(diskProfile, null, plan, avoid, StoragePoolAllocator.RETURN_UPTO_ALL, + false, null); + if (CollectionUtils.isNotEmpty(pools)) { + for (StoragePool pool : pools) { + if (allPoolIds.contains(pool.getId()) && !addedPoolIds.contains(pool.getId())) { + suitablePools.add(pool); + addedPoolIds.add(pool.getId()); + } + } + } + } catch (Exception e) { + logger.warn("Allocator {} failed to find storage pools for detached volume {} due to {}", + allocator.getClass().getSimpleName(), volume.getId(), e.getMessage()); + } } } + return suitablePools; } From f48cc9cc8ac643435a8e0fb32945b969bbe1e457 Mon Sep 17 00:00:00 2001 From: Pearl Dsilva Date: Fri, 16 Jan 2026 15:01:44 -0500 Subject: [PATCH 2/3] tweak logic to consider tags and rule tags at zone and cluster scope --- .../cloud/server/ManagementServerImpl.java | 89 +++++++++++++------ 1 file changed, 62 insertions(+), 27 deletions(-) diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index bdf539bdd8b0..ffbd8825e3ff 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -767,6 +767,7 @@ import com.cloud.storage.Storage; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; +import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.Volume; import com.cloud.storage.VolumeApiServiceImpl; @@ -1998,42 +1999,76 @@ private List findAllSuitableStoragePoolsForDetachedVolume(Volume vo return suitablePools; } - HypervisorType hypervisorType = getHypervisorType(null, srcPool); - List clusters = _clusterDao.listByDcHyType(srcPool.getDataCenterId(), hypervisorType.toString()); - DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId); - DiskProfile diskProfile = new DiskProfile(volume, diskOffering, hypervisorType); + String[] tagsArray = diskOffering.getTagsArray(); + List tags = (tagsArray != null && tagsArray.length > 0) ? Arrays.asList(tagsArray) : new ArrayList<>(); - ExcludeList avoid = new ExcludeList(); + HypervisorType hypervisorType = getHypervisorType(null, srcPool); + Long dcId = srcPool.getDataCenterId(); + + logger.debug("Finding suitable pools for detached volume {} with offering tags: {}, hypervisor: {}", + volume.getUuid(), tags, hypervisorType); + + // Create a map for quick lookup and deduplication + Map allPoolsMap = allPools.stream() + .collect(Collectors.toMap(StoragePool::getId, pool -> pool)); + Set matchingPoolIds = new HashSet<>(); + + List zonePoolsLiteral = _poolDao.findZoneWideStoragePoolsByTags(dcId, + tags.isEmpty() ? null : tags.toArray(new String[0]), true); + for (StoragePoolVO pool : zonePoolsLiteral) { + if (pool.getHypervisor() == null || pool.getHypervisor().equals(HypervisorType.Any) || + pool.getHypervisor().equals(hypervisorType)) { + matchingPoolIds.add(pool.getId()); + logger.debug("Found zone-wide pool with literal tags: {} ({})", pool.getName(), pool.getId()); + } + } - Set allPoolIds = allPools.stream() - .map(StoragePool::getId) - .collect(Collectors.toSet()); - Set addedPoolIds = new HashSet<>(); + List zonePoolsRules = _poolJoinDao.findStoragePoolByScopeAndRuleTags(dcId, null, null, + ScopeType.ZONE, tags); + for (StoragePoolVO pool : zonePoolsRules) { + StoragePoolVO poolVO = _poolDao.findById(pool.getId()); + if (poolVO != null && (poolVO.getHypervisor() == null || poolVO.getHypervisor().equals(HypervisorType.Any) || + poolVO.getHypervisor().equals(hypervisorType))) { + matchingPoolIds.add(pool.getId()); + logger.debug("Found zone-wide pool with rule-based tags: {} ({})", pool.getName(), pool.getId()); + } + } - for (Cluster cluster : clusters) { - DataCenterDeployment plan = new DataCenterDeployment(srcPool.getDataCenterId(), cluster.getPodId(), cluster.getId(), - null, null, null, null); + List clusters = _clusterDao.listByDcHyType(dcId, hypervisorType.toString()); + for (ClusterVO cluster : clusters) { + List clusterPoolsLiteral = _poolDao.findPoolsByTags(dcId, cluster.getPodId(), + cluster.getId(), tags.isEmpty() ? null : tags.toArray(new String[0]), true, + VolumeApiServiceImpl.storageTagRuleExecutionTimeout.value()); + for (StoragePoolVO pool : clusterPoolsLiteral) { + matchingPoolIds.add(pool.getId()); + logger.debug("Found cluster-scoped pool with literal tags: {} ({}) in cluster {}", + pool.getName(), pool.getId(), cluster.getName()); + } + List clusterPoolsRules = _poolJoinDao.findStoragePoolByScopeAndRuleTags(dcId, + cluster.getPodId(), cluster.getId(), ScopeType.CLUSTER, tags); + for (StoragePoolVO pool : clusterPoolsRules) { + matchingPoolIds.add(pool.getId()); + logger.debug("Found cluster-scoped pool with rule-based tags: {} ({}) in cluster {}", + pool.getName(), pool.getId(), cluster.getName()); + } + } - for (StoragePoolAllocator allocator : _storagePoolAllocators) { - try { - List pools = allocator.allocateToPool(diskProfile, null, plan, avoid, StoragePoolAllocator.RETURN_UPTO_ALL, - false, null); - if (CollectionUtils.isNotEmpty(pools)) { - for (StoragePool pool : pools) { - if (allPoolIds.contains(pool.getId()) && !addedPoolIds.contains(pool.getId())) { - suitablePools.add(pool); - addedPoolIds.add(pool.getId()); - } - } - } - } catch (Exception e) { - logger.warn("Allocator {} failed to find storage pools for detached volume {} due to {}", - allocator.getClass().getSimpleName(), volume.getId(), e.getMessage()); + for (Long poolId : matchingPoolIds) { + if (allPoolsMap.containsKey(poolId)) { + StoragePool pool = allPoolsMap.get(poolId); + if (StoragePoolStatus.Up.equals(pool.getStatus())) { + suitablePools.add(pool); + logger.debug("Added pool {} to suitable pools", pool.getName()); + } else { + logger.debug("Skipping pool {} - status is {}, not Up", pool.getName(), pool.getStatus()); } } } + logger.debug("Found {} suitable pools out of {} total pools for detached volume {}", + suitablePools.size(), allPools.size(), volume.getUuid()); + return suitablePools; } From 89d66a1c612fced7f958fc71b9b8cc129d96a2c1 Mon Sep 17 00:00:00 2001 From: Pearl Dsilva Date: Fri, 16 Jan 2026 15:11:01 -0500 Subject: [PATCH 3/3] tweak logic to consider tags and rule tags at zone and cluster scope --- .../cloud/server/ManagementServerImpl.java | 41 ++++++++----------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index ffbd8825e3ff..c86283142b45 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -2009,60 +2009,53 @@ private List findAllSuitableStoragePoolsForDetachedVolume(Volume vo logger.debug("Finding suitable pools for detached volume {} with offering tags: {}, hypervisor: {}", volume.getUuid(), tags, hypervisorType); - // Create a map for quick lookup and deduplication - Map allPoolsMap = allPools.stream() - .collect(Collectors.toMap(StoragePool::getId, pool -> pool)); Set matchingPoolIds = new HashSet<>(); - List zonePoolsLiteral = _poolDao.findZoneWideStoragePoolsByTags(dcId, + List zonePoolsStandard = _poolDao.findZoneWideStoragePoolsByTags(dcId, tags.isEmpty() ? null : tags.toArray(new String[0]), true); - for (StoragePoolVO pool : zonePoolsLiteral) { + for (StoragePoolVO pool : zonePoolsStandard) { if (pool.getHypervisor() == null || pool.getHypervisor().equals(HypervisorType.Any) || pool.getHypervisor().equals(hypervisorType)) { matchingPoolIds.add(pool.getId()); - logger.debug("Found zone-wide pool with literal tags: {} ({})", pool.getName(), pool.getId()); + logger.debug("Found zone-wide pool with standard tags: {} ({})", pool.getName(), pool.getId()); } } - List zonePoolsRules = _poolJoinDao.findStoragePoolByScopeAndRuleTags(dcId, null, null, + List zonePoolsFlexible = _poolJoinDao.findStoragePoolByScopeAndRuleTags(dcId, null, null, ScopeType.ZONE, tags); - for (StoragePoolVO pool : zonePoolsRules) { + for (StoragePoolVO pool : zonePoolsFlexible) { StoragePoolVO poolVO = _poolDao.findById(pool.getId()); if (poolVO != null && (poolVO.getHypervisor() == null || poolVO.getHypervisor().equals(HypervisorType.Any) || poolVO.getHypervisor().equals(hypervisorType))) { matchingPoolIds.add(pool.getId()); - logger.debug("Found zone-wide pool with rule-based tags: {} ({})", pool.getName(), pool.getId()); + logger.debug("Found zone-wide pool with flexible tags: {} ({})", pool.getName(), pool.getId()); } } List clusters = _clusterDao.listByDcHyType(dcId, hypervisorType.toString()); for (ClusterVO cluster : clusters) { - List clusterPoolsLiteral = _poolDao.findPoolsByTags(dcId, cluster.getPodId(), + List clusterPoolsStandard = _poolDao.findPoolsByTags(dcId, cluster.getPodId(), cluster.getId(), tags.isEmpty() ? null : tags.toArray(new String[0]), true, VolumeApiServiceImpl.storageTagRuleExecutionTimeout.value()); - for (StoragePoolVO pool : clusterPoolsLiteral) { + for (StoragePoolVO pool : clusterPoolsStandard) { matchingPoolIds.add(pool.getId()); - logger.debug("Found cluster-scoped pool with literal tags: {} ({}) in cluster {}", + logger.debug("Found cluster-scoped pool with standard tags: {} ({}) in cluster {}", pool.getName(), pool.getId(), cluster.getName()); } - List clusterPoolsRules = _poolJoinDao.findStoragePoolByScopeAndRuleTags(dcId, + + List clusterPoolsFlexible = _poolJoinDao.findStoragePoolByScopeAndRuleTags(dcId, cluster.getPodId(), cluster.getId(), ScopeType.CLUSTER, tags); - for (StoragePoolVO pool : clusterPoolsRules) { + for (StoragePoolVO pool : clusterPoolsFlexible) { matchingPoolIds.add(pool.getId()); - logger.debug("Found cluster-scoped pool with rule-based tags: {} ({}) in cluster {}", + logger.debug("Found cluster-scoped pool with flexible tags: {} ({}) in cluster {}", pool.getName(), pool.getId(), cluster.getName()); } } - for (Long poolId : matchingPoolIds) { - if (allPoolsMap.containsKey(poolId)) { - StoragePool pool = allPoolsMap.get(poolId); - if (StoragePoolStatus.Up.equals(pool.getStatus())) { - suitablePools.add(pool); - logger.debug("Added pool {} to suitable pools", pool.getName()); - } else { - logger.debug("Skipping pool {} - status is {}, not Up", pool.getName(), pool.getStatus()); - } + for (StoragePool pool : allPools) { + if (matchingPoolIds.contains(pool.getId()) && StoragePoolStatus.Up.equals(pool.getStatus())) { + suitablePools.add(pool); + logger.debug("Added pool {} to suitable pools", pool.getName()); } }