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 5c7348cbe6c3..7a29ce81002b 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 @@ -432,4 +432,8 @@ void connectHostsToPool(DataStore primaryStore, List hostIds, Scope scope, String[] getStorageAccessGroups(Long zoneId, Long podId, Long clusterId, Long hostId); CapacityVO getObjectStorageUsedStats(Long zoneId); + + static ConfigKey getMountDisabledStoragePool() { + return MountDisabledStoragePool; + } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java index 57b5f6461c6a..fd85dba1efcc 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java @@ -90,6 +90,22 @@ public interface PrimaryDataStoreDao extends GenericDao { List findDisabledPoolsByScope(long dcId, Long podId, Long clusterId, ScopeType scope); + /** + * Finds disabled storage pools within the specified scope that are associated with the given storage access groups. + * This method is used to locate storage pools with 'Disabled' status that match specific storage access group + * filters. + * + * @param dcId the data center ID. + * @param podId the pod ID. + * @param clusterId the cluster ID. + * @param scope ZONE, CLUSTER OR HOST type scope + * @param storageAccessGroups array of storage access group names to match against. + * Only pools associated with these access groups will be returned. + * If null or empty, returns an empty list. + * @return a list of {@link StoragePoolVO} objects. + */ + List findDisabledPoolsByScopeAndAccessGroups(long dcId, Long podId, Long clusterId, ScopeType scope, String[] storageAccessGroups); + /** * Find pool by UUID. * @@ -167,7 +183,19 @@ Pair, Integer> searchForIdsAndCount(Long storagePoolId, String storag List listByIds(List ids); - List findStoragePoolsByEmptyStorageAccessGroups(Long dcId, Long podId, Long clusterId, ScopeType scope, HypervisorType hypervisorType); + /** + * Finds storage pools that have no storage access groups associated with them within the specified criteria. + * This method identifies storage pools without access group restrictions. + * + * @param dcId the data center ID. Can be null to include all data centers. + * @param podId the pod ID. Can be null to include all pods. + * @param clusterId the cluster ID. Can be null to include all clusters. + * @param scope ZONE, CLUSTER or HOST type scope + * @param hypervisorType the hypervisor type filter. Can be null to include all hypervisor types. + * @param status the storage pool status to filter by. + * @return a list of {@link StoragePoolVO} objects that have no storage access groups associated. + */ + List findStoragePoolsByEmptyStorageAccessGroups(Long dcId, Long podId, Long clusterId, ScopeType scope, HypervisorType hypervisorType, StoragePoolStatus status); List findPoolsByStorageTypeAndZone(Storage.StoragePoolType storageType, Long zoneId); diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java index b5d6415e3a1a..4d0e581826f4 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java @@ -54,6 +54,7 @@ import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.commons.lang3.ArrayUtils; @DB() public class PrimaryDataStoreDaoImpl extends GenericDaoBase implements PrimaryDataStoreDao { @@ -86,6 +87,37 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase private final String ZoneWideStorageAccessGroupsWithHypervisorTypeSqlPrefix = "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_and_access_group_map ON storage_pool.id = storage_pool_and_access_group_map.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' and storage_pool.hypervisor = ? and storage_pool.data_center_id = ? and storage_pool.scope = ? and ("; private final String ZoneWideStorageAccessGroupsWithHypervisorTypeSqlSuffix = ") GROUP BY storage_pool_and_access_group_map.pool_id"; + /** + * SQL query prefix to find zone-wide disabled storage pools that are associated with specific storage access groups. + */ + private final String ZoneWideDisabledStorageAccessGroupsSqlPrefix = + "SELECT storage_pool.* FROM storage_pool " + + "LEFT JOIN storage_pool_and_access_group_map ON storage_pool.id = storage_pool_and_access_group_map.pool_id " + + "WHERE storage_pool.removed IS NULL " + + " AND storage_pool.status = 'Disabled' " + + " AND storage_pool.data_center_id = ? " + + " AND storage_pool.scope = ? " + + " AND ("; + + /** + * SQL query prefix to find disabled storage pools with specific storage access groups. The query is used at the HOST scope. + */ + private final String DisabledStorageAccessGroupsForHostConnectionSqlPrefix = + "SELECT storage_pool.* FROM storage_pool " + + "LEFT JOIN storage_pool_and_access_group_map ON storage_pool.id = storage_pool_and_access_group_map.pool_id " + + "WHERE storage_pool.removed IS NULL " + + " AND storage_pool.status = 'Disabled' " + + " AND storage_pool.data_center_id = ? " + + " AND (storage_pool.pod_id = ? OR storage_pool.pod_id IS NULL) " + + " AND storage_pool.scope = ? " + + " AND ("; + + /** + * SQL query suffix for disabled storage access groups queries. This suffix completes the query by grouping + * results by pool_id. + */ + private final String DisabledStorageAccessGroupsSqlSuffix = ") GROUP BY storage_pool_and_access_group_map.pool_id"; + // Storage tags are now separate from storage_pool_details, leaving only details on that table protected final String TagsSqlPrefix = "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_tags ON storage_pool.id = storage_pool_tags.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' AND storage_pool_tags.is_tag_a_rule = 0 and storage_pool.data_center_id = ? and (storage_pool.pod_id = ? or storage_pool.pod_id is null) and storage_pool.scope = ? and ("; protected final String TagsSqlSuffix = ") GROUP BY storage_pool_tags.pool_id HAVING COUNT(storage_pool_tags.tag) >= ?"; @@ -568,6 +600,26 @@ public List findDisabledPoolsByScope(long dcId, Long podId, Long return storagePools; } + @Override + public List findDisabledPoolsByScopeAndAccessGroups(long dcId, Long podId, Long clusterId, ScopeType scope, String[] storageAccessGroups) { + if (ArrayUtils.isEmpty(storageAccessGroups)) { + return List.of(); + } + + List storagePools = null; + String sqlValues = getSqlValuesFromStorageAccessGroups(storageAccessGroups); + + if (scope == ScopeType.ZONE) { + String sql = getSqlPreparedStatement(ZoneWideDisabledStorageAccessGroupsSqlPrefix, DisabledStorageAccessGroupsSqlSuffix, sqlValues, null); + storagePools = searchStoragePoolsPreparedStatement(sql, dcId, null, null, scope, null); + } else if ((scope == ScopeType.CLUSTER || scope == ScopeType.HOST) && podId != null && clusterId != null) { + String sql = getSqlPreparedStatement(DisabledStorageAccessGroupsForHostConnectionSqlPrefix, DisabledStorageAccessGroupsSqlSuffix, sqlValues, clusterId); + storagePools = searchStoragePoolsPreparedStatement(sql, dcId, podId, clusterId, scope, null); + } + + return storagePools; + } + @Override public List findLocalStoragePoolsByTags(long dcId, long podId, Long clusterId, String[] tags, boolean validateTagRule) { return findLocalStoragePoolsByTags(dcId, podId, clusterId, tags, validateTagRule, null); @@ -691,7 +743,7 @@ public List findZoneWideStoragePoolsByAccessGroupsAndHypervisorTy } @Override - public List findStoragePoolsByEmptyStorageAccessGroups(Long dcId, Long podId, Long clusterId, ScopeType scope, HypervisorType hypervisorType) { + public List findStoragePoolsByEmptyStorageAccessGroups(Long dcId, Long podId, Long clusterId, ScopeType scope, HypervisorType hypervisorType, StoragePoolStatus status) { SearchBuilder poolSearch = createSearchBuilder(); SearchBuilder storageAccessGroupsPoolSearch = _storagePoolAccessGroupMapDao.createSearchBuilder(); // Set criteria for pools @@ -709,7 +761,6 @@ public List findStoragePoolsByEmptyStorageAccessGroups(Long dcId, SearchCriteria sc = poolSearch.create(); sc.setParameters("scope", scope.toString()); - sc.setParameters("status", Status.Up.toString()); if (dcId != null) { sc.setParameters("datacenterid", dcId); @@ -727,6 +778,10 @@ public List findStoragePoolsByEmptyStorageAccessGroups(Long dcId, sc.setParameters("hypervisortype", hypervisorType); } + if (status != null) { + sc.setParameters("status", status.toString()); + } + return listBy(sc); } diff --git a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java index 265323547204..b85fd7f4fb35 100755 --- a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java +++ b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java @@ -2744,8 +2744,8 @@ protected List getStoragePoolsByAccessGroups(Long dcId, Long podI allPoolsByTags.addAll(_storagePoolDao.findPoolsByAccessGroupsForHostConnection(dcId, podId, clusterId, ScopeType.CLUSTER, storageAccessGroups)); allPoolsByTags.addAll(_storagePoolDao.findZoneWideStoragePoolsByAccessGroupsForHostConnection(dcId, storageAccessGroups)); if (includeEmptyTags) { - allPoolsByTags.addAll(_storagePoolDao.findStoragePoolsByEmptyStorageAccessGroups(dcId, podId, clusterId, ScopeType.CLUSTER, null)); - allPoolsByTags.addAll(_storagePoolDao.findStoragePoolsByEmptyStorageAccessGroups(dcId, null, null, ScopeType.ZONE, null)); + allPoolsByTags.addAll(_storagePoolDao.findStoragePoolsByEmptyStorageAccessGroups(dcId, podId, clusterId, ScopeType.CLUSTER, null, StoragePoolStatus.Up)); + allPoolsByTags.addAll(_storagePoolDao.findStoragePoolsByEmptyStorageAccessGroups(dcId, null, null, ScopeType.ZONE, null, StoragePoolStatus.Up)); } return allPoolsByTags; @@ -2753,8 +2753,8 @@ protected List getStoragePoolsByAccessGroups(Long dcId, Long podI private List getStoragePoolsByEmptyStorageAccessGroups(Long dcId, Long podId, Long clusterId) { List allPoolsByTags = new ArrayList<>(); - allPoolsByTags.addAll(_storagePoolDao.findStoragePoolsByEmptyStorageAccessGroups(dcId, podId, clusterId, ScopeType.CLUSTER, null)); - allPoolsByTags.addAll(_storagePoolDao.findStoragePoolsByEmptyStorageAccessGroups(dcId, null, null, ScopeType.ZONE, null)); + allPoolsByTags.addAll(_storagePoolDao.findStoragePoolsByEmptyStorageAccessGroups(dcId, podId, clusterId, ScopeType.CLUSTER, null, StoragePoolStatus.Up)); + allPoolsByTags.addAll(_storagePoolDao.findStoragePoolsByEmptyStorageAccessGroups(dcId, null, null, ScopeType.ZONE, null, StoragePoolStatus.Up)); return allPoolsByTags; } diff --git a/server/src/main/java/com/cloud/storage/listener/StoragePoolMonitor.java b/server/src/main/java/com/cloud/storage/listener/StoragePoolMonitor.java index 2f9750edee2d..d7f20c03671b 100644 --- a/server/src/main/java/com/cloud/storage/listener/StoragePoolMonitor.java +++ b/server/src/main/java/com/cloud/storage/listener/StoragePoolMonitor.java @@ -25,6 +25,7 @@ import com.cloud.dc.dao.HostPodDao; import com.cloud.exception.StorageConflictException; import com.cloud.storage.StorageManager; +import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.Profiler; @@ -123,21 +124,21 @@ public void processConnect(Host host, StartupCommand cmd, boolean forRebalance) List pools = new ArrayList<>(); // SAG -> Storage Access Group if (ArrayUtils.isEmpty(sags)) { - List clusterStoragePoolsByEmptySAGs = _poolDao.findStoragePoolsByEmptyStorageAccessGroups(host.getDataCenterId(), host.getPodId(), host.getClusterId(), ScopeType.CLUSTER, null); - List storagePoolsByEmptySAGs = _poolDao.findStoragePoolsByEmptyStorageAccessGroups(host.getDataCenterId(), null, null, ScopeType.ZONE, null); - List zoneStoragePoolsByHypervisor = _poolDao.findStoragePoolsByEmptyStorageAccessGroups(host.getDataCenterId(), null, null, ScopeType.ZONE, scCmd.getHypervisorType()); + List clusterStoragePoolsByEmptySAGs = _poolDao.findStoragePoolsByEmptyStorageAccessGroups(host.getDataCenterId(), host.getPodId(), host.getClusterId(), ScopeType.CLUSTER, null, StoragePoolStatus.Up); + List storagePoolsByEmptySAGs = _poolDao.findStoragePoolsByEmptyStorageAccessGroups(host.getDataCenterId(), null, null, ScopeType.ZONE, null, StoragePoolStatus.Up); + List zoneStoragePoolsByHypervisor = _poolDao.findStoragePoolsByEmptyStorageAccessGroups(host.getDataCenterId(), null, null, ScopeType.ZONE, scCmd.getHypervisorType(), StoragePoolStatus.Up); storagePoolsByEmptySAGs.retainAll(zoneStoragePoolsByHypervisor); pools.addAll(storagePoolsByEmptySAGs); pools.addAll(clusterStoragePoolsByEmptySAGs); - List zoneStoragePoolsByAnyHypervisor = _poolDao.findStoragePoolsByEmptyStorageAccessGroups(host.getDataCenterId(), null, null, ScopeType.ZONE, HypervisorType.Any); + List zoneStoragePoolsByAnyHypervisor = _poolDao.findStoragePoolsByEmptyStorageAccessGroups(host.getDataCenterId(), null, null, ScopeType.ZONE, HypervisorType.Any, StoragePoolStatus.Up); pools.addAll(zoneStoragePoolsByAnyHypervisor); } else { List storagePoolsBySAGs = new ArrayList<>(); List clusterStoragePoolsBySAGs = _poolDao.findPoolsByAccessGroupsForHostConnection(host.getDataCenterId(), host.getPodId(), host.getClusterId(), ScopeType.CLUSTER, sags); - List clusterStoragePoolsByEmptySAGs = _poolDao.findStoragePoolsByEmptyStorageAccessGroups(host.getDataCenterId(), host.getPodId(), host.getClusterId(), ScopeType.CLUSTER, null); + List clusterStoragePoolsByEmptySAGs = _poolDao.findStoragePoolsByEmptyStorageAccessGroups(host.getDataCenterId(), host.getPodId(), host.getClusterId(), ScopeType.CLUSTER, null, StoragePoolStatus.Up); List zoneStoragePoolsBySAGs = _poolDao.findZoneWideStoragePoolsByAccessGroupsAndHypervisorTypeForHostConnection(host.getDataCenterId(), sags, scCmd.getHypervisorType()); List zoneStoragePoolsByHypervisorTypeAny = _poolDao.findZoneWideStoragePoolsByAccessGroupsAndHypervisorTypeForHostConnection(host.getDataCenterId(), sags, HypervisorType.Any); - List zoneStoragePoolsByEmptySAGs = _poolDao.findStoragePoolsByEmptyStorageAccessGroups(host.getDataCenterId(), null, null, ScopeType.ZONE, null); + List zoneStoragePoolsByEmptySAGs = _poolDao.findStoragePoolsByEmptyStorageAccessGroups(host.getDataCenterId(), null, null, ScopeType.ZONE, null, StoragePoolStatus.Up); storagePoolsBySAGs.addAll(zoneStoragePoolsBySAGs); storagePoolsBySAGs.addAll(zoneStoragePoolsByEmptySAGs); @@ -148,13 +149,15 @@ public void processConnect(Host host, StartupCommand cmd, boolean forRebalance) } // get the zone wide disabled pools list if global setting is true. - if (StorageManager.MountDisabledStoragePool.value()) { - pools.addAll(_poolDao.findDisabledPoolsByScope(host.getDataCenterId(), null, null, ScopeType.ZONE)); + if (StorageManager.getMountDisabledStoragePool().value()) { + pools.addAll(_poolDao.findDisabledPoolsByScopeAndAccessGroups(host.getDataCenterId(), null, null, ScopeType.ZONE, sags)); + pools.addAll(_poolDao.findStoragePoolsByEmptyStorageAccessGroups(host.getDataCenterId(), null, null, ScopeType.ZONE, null, StoragePoolStatus.Disabled)); } // get the cluster wide disabled pool list - if (StorageManager.MountDisabledStoragePool.valueIn(host.getClusterId())) { - pools.addAll(_poolDao.findDisabledPoolsByScope(host.getDataCenterId(), host.getPodId(), host.getClusterId(), ScopeType.CLUSTER)); + if (StorageManager.getMountDisabledStoragePool().valueIn(host.getClusterId())) { + pools.addAll(_poolDao.findDisabledPoolsByScopeAndAccessGroups(host.getDataCenterId(), host.getPodId(), host.getClusterId(), ScopeType.CLUSTER, sags)); + pools.addAll(_poolDao.findStoragePoolsByEmptyStorageAccessGroups(host.getDataCenterId(), host.getPodId(), host.getClusterId(), ScopeType.CLUSTER, null, StoragePoolStatus.Disabled)); } List previouslyConnectedPools = new ArrayList<>(); diff --git a/server/src/test/java/com/cloud/storage/listener/StoragePoolMonitorTest.java b/server/src/test/java/com/cloud/storage/listener/StoragePoolMonitorTest.java index 7421eb7ae2d9..0c729b364d19 100644 --- a/server/src/test/java/com/cloud/storage/listener/StoragePoolMonitorTest.java +++ b/server/src/test/java/com/cloud/storage/listener/StoragePoolMonitorTest.java @@ -22,17 +22,27 @@ import com.cloud.hypervisor.Hypervisor; import com.cloud.storage.ScopeType; import com.cloud.storage.Storage; +import com.cloud.storage.StorageManager; import com.cloud.storage.StorageManagerImpl; import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.dao.StoragePoolHostDao; +import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockedStatic; import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; +import static org.mockito.Mockito.mock; + +@RunWith(MockitoJUnitRunner.class) public class StoragePoolMonitorTest { private StorageManagerImpl storageManager; @@ -45,9 +55,9 @@ public class StoragePoolMonitorTest { @Before public void setUp() throws Exception { - storageManager = Mockito.mock(StorageManagerImpl.class); - poolDao = Mockito.mock(PrimaryDataStoreDao.class); - storagePoolHostDao = Mockito.mock(StoragePoolHostDao.class); + storageManager = mock(StorageManagerImpl.class); + poolDao = mock(PrimaryDataStoreDao.class); + storagePoolHostDao = mock(StoragePoolHostDao.class); storagePoolMonitor = new StoragePoolMonitor(storageManager, poolDao, storagePoolHostDao, null); host = new HostVO("some-uuid"); @@ -62,21 +72,21 @@ public void setUp() throws Exception { @Test public void testProcessConnectStoragePoolNormal() throws Exception { - HostVO hostMock = Mockito.mock(HostVO.class); - StartupRoutingCommand startupRoutingCommand = Mockito.mock(StartupRoutingCommand.class); - StoragePoolVO poolMock = Mockito.mock(StoragePoolVO.class); - Mockito.when(poolMock.getScope()).thenReturn(ScopeType.CLUSTER); - Mockito.when(poolMock.getStatus()).thenReturn(StoragePoolStatus.Up); - Mockito.when(poolMock.getId()).thenReturn(123L); - Mockito.when(poolMock.getPoolType()).thenReturn(Storage.StoragePoolType.Filesystem); + HostVO hostMock = mock(HostVO.class); + StartupRoutingCommand startupRoutingCommand = mock(StartupRoutingCommand.class); + StoragePoolVO poolMock = mock(StoragePoolVO.class); + Mockito.lenient().when(poolMock.getScope()).thenReturn(ScopeType.CLUSTER); + Mockito.lenient().when(poolMock.getStatus()).thenReturn(StoragePoolStatus.Up); + Mockito.lenient().when(poolMock.getId()).thenReturn(123L); + Mockito.lenient().when(poolMock.getPoolType()).thenReturn(Storage.StoragePoolType.Filesystem); Mockito.when(hostMock.getDataCenterId()).thenReturn(1L); Mockito.when(hostMock.getPodId()).thenReturn(1L); Mockito.when(hostMock.getClusterId()).thenReturn(1L); Mockito.when(startupRoutingCommand.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); - Mockito.when(poolDao.findStoragePoolsByEmptyStorageAccessGroups(1L, 1L, 1L, ScopeType.CLUSTER, null)).thenReturn(Collections.singletonList(pool)); - Mockito.when(poolDao.findStoragePoolsByEmptyStorageAccessGroups(1L, null, null, ScopeType.ZONE, null)).thenReturn(Collections.emptyList()); - Mockito.when(poolDao.findStoragePoolsByEmptyStorageAccessGroups(1L, null, null, ScopeType.ZONE, Hypervisor.HypervisorType.KVM)).thenReturn(Collections.emptyList()); - Mockito.when(poolDao.findStoragePoolsByEmptyStorageAccessGroups(1L, null, null, ScopeType.ZONE, Hypervisor.HypervisorType.Any)).thenReturn(Collections.emptyList()); + Mockito.when(poolDao.findStoragePoolsByEmptyStorageAccessGroups(1L, 1L, 1L, ScopeType.CLUSTER, null, StoragePoolStatus.Up)).thenReturn(Collections.singletonList(pool)); + Mockito.when(poolDao.findStoragePoolsByEmptyStorageAccessGroups(1L, null, null, ScopeType.ZONE, null, StoragePoolStatus.Up)).thenReturn(Collections.emptyList()); + Mockito.when(poolDao.findStoragePoolsByEmptyStorageAccessGroups(1L, null, null, ScopeType.ZONE, Hypervisor.HypervisorType.KVM, StoragePoolStatus.Up)).thenReturn(Collections.emptyList()); + Mockito.when(poolDao.findStoragePoolsByEmptyStorageAccessGroups(1L, null, null, ScopeType.ZONE, Hypervisor.HypervisorType.Any, StoragePoolStatus.Up)).thenReturn(Collections.emptyList()); Mockito.doReturn(true).when(storageManager).connectHostToSharedPool(hostMock, 123L); storagePoolMonitor.processConnect(hostMock, startupRoutingCommand, false); @@ -87,11 +97,81 @@ public void testProcessConnectStoragePoolNormal() throws Exception { @Test public void testProcessConnectStoragePoolFailureOnHost() throws Exception { - Mockito.when(poolDao.listBy(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.any(ScopeType.class))).thenReturn(Collections.singletonList(pool)); - Mockito.when(poolDao.findZoneWideStoragePoolsByTags(Mockito.anyLong(), Mockito.any(String[].class), Mockito.anyBoolean())).thenReturn(Collections.emptyList()); - Mockito.when(poolDao.findZoneWideStoragePoolsByHypervisor(Mockito.anyLong(), Mockito.any(Hypervisor.HypervisorType.class))).thenReturn(Collections.emptyList()); - Mockito.doThrow(new StorageUnavailableException("unable to mount storage", 123L)).when(storageManager).connectHostToSharedPool(Mockito.any(), Mockito.anyLong()); + Mockito.lenient().when(poolDao.listBy(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.any(ScopeType.class))).thenReturn(Collections.singletonList(pool)); + Mockito.lenient().when(poolDao.findZoneWideStoragePoolsByTags(Mockito.anyLong(), Mockito.any(String[].class), Mockito.anyBoolean())).thenReturn(Collections.emptyList()); + Mockito.lenient().when(poolDao.findZoneWideStoragePoolsByHypervisor(Mockito.anyLong(), Mockito.any(Hypervisor.HypervisorType.class))).thenReturn(Collections.emptyList()); + Mockito.lenient().doThrow(new StorageUnavailableException("unable to mount storage", 123L)).when(storageManager).connectHostToSharedPool(Mockito.any(), Mockito.anyLong()); storagePoolMonitor.processConnect(host, cmd, false); } + + @Test + public void testProcessConnectWithMountDisabledStoragePoolEnabled() throws Exception { + StoragePoolVO disabledZonePool = new StoragePoolVO(); + disabledZonePool.setId(200L); + disabledZonePool.setScope(ScopeType.ZONE); + disabledZonePool.setStatus(StoragePoolStatus.Disabled); + disabledZonePool.setPoolType(Storage.StoragePoolType.NetworkFilesystem); + + StoragePoolVO disabledClusterPool = new StoragePoolVO(); + disabledClusterPool.setId(201L); + disabledClusterPool.setScope(ScopeType.CLUSTER); + disabledClusterPool.setStatus(StoragePoolStatus.Disabled); + disabledClusterPool.setPoolType(Storage.StoragePoolType.NetworkFilesystem); + HostVO hostMock = mock(HostVO.class); + StartupRoutingCommand startupRoutingCommand = mock(StartupRoutingCommand.class); + Mockito.when(hostMock.getDataCenterId()).thenReturn(1L); + Mockito.when(hostMock.getPodId()).thenReturn(1L); + Mockito.when(hostMock.getClusterId()).thenReturn(1L); + Mockito.when(hostMock.getId()).thenReturn(1L); + Mockito.when(startupRoutingCommand.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + + try (MockedStatic storageManagerMockedStatic = Mockito.mockStatic(StorageManager.class)) { + ConfigKey mockConfigKey = mock(ConfigKey.class); + Mockito.when(mockConfigKey.value()).thenReturn(true); + Mockito.when(mockConfigKey.valueIn(1L)).thenReturn(true); + + storageManagerMockedStatic.when(StorageManager::getMountDisabledStoragePool).thenReturn(mockConfigKey); + + Mockito.when(poolDao.findStoragePoolsByEmptyStorageAccessGroups(1L, 1L, 1L, ScopeType.CLUSTER, null, StoragePoolStatus.Up)) + .thenReturn(new ArrayList<>()); + Mockito.when(poolDao.findStoragePoolsByEmptyStorageAccessGroups(1L, null, null, ScopeType.ZONE, null, StoragePoolStatus.Up)) + .thenReturn(new ArrayList<>()); + Mockito.when(poolDao.findStoragePoolsByEmptyStorageAccessGroups(1L, null, null, ScopeType.ZONE, Hypervisor.HypervisorType.KVM, StoragePoolStatus.Up)) + .thenReturn(new ArrayList<>()); + Mockito.when(poolDao.findStoragePoolsByEmptyStorageAccessGroups(1L, null, null, ScopeType.ZONE, Hypervisor.HypervisorType.Any, StoragePoolStatus.Up)) + .thenReturn(new ArrayList<>()); + + List zoneDisabledPoolsBySAG = new ArrayList<>(); + zoneDisabledPoolsBySAG.add(disabledZonePool); + Mockito.when(poolDao.findDisabledPoolsByScopeAndAccessGroups(1L, null, null, ScopeType.ZONE, new String[0])) + .thenReturn(zoneDisabledPoolsBySAG); + Mockito.when(poolDao.findStoragePoolsByEmptyStorageAccessGroups(1L, null, null, ScopeType.ZONE, null, StoragePoolStatus.Disabled)) + .thenReturn(new ArrayList<>()); + + List clusterDisabledPoolsBySAG = new ArrayList<>(); + clusterDisabledPoolsBySAG.add(disabledClusterPool); + Mockito.when(poolDao.findDisabledPoolsByScopeAndAccessGroups(1L, 1L, 1L, ScopeType.CLUSTER, new String[0])) + .thenReturn(clusterDisabledPoolsBySAG); + Mockito.when(poolDao.findStoragePoolsByEmptyStorageAccessGroups(1L, 1L, 1L, ScopeType.CLUSTER, null, StoragePoolStatus.Disabled)) + .thenReturn(new ArrayList<>()); + + Mockito.when(storageManager.getStorageAccessGroups(null, null, null, 1L)).thenReturn(new String[0]); + Mockito.when(storageManager.findStoragePoolsConnectedToHost(1L)).thenReturn(Collections.emptyList()); + Mockito.doReturn(true).when(storageManager).connectHostToSharedPool(hostMock, 200L); + Mockito.doReturn(true).when(storageManager).connectHostToSharedPool(hostMock, 201L); + + storagePoolMonitor.processConnect(hostMock, startupRoutingCommand, false); + + Mockito.verify(poolDao, Mockito.times(1)).findDisabledPoolsByScopeAndAccessGroups(1L, null, null, ScopeType.ZONE, new String[0]); + Mockito.verify(poolDao, Mockito.times(1)).findStoragePoolsByEmptyStorageAccessGroups(1L, null, null, ScopeType.ZONE, null, StoragePoolStatus.Disabled); + Mockito.verify(poolDao, Mockito.times(1)).findDisabledPoolsByScopeAndAccessGroups(1L, 1L, 1L, ScopeType.CLUSTER, new String[0]); + Mockito.verify(poolDao, Mockito.times(1)).findStoragePoolsByEmptyStorageAccessGroups(1L, 1L, 1L, ScopeType.CLUSTER, null, StoragePoolStatus.Disabled); + + Mockito.verify(storageManager, Mockito.times(1)).connectHostToSharedPool(Mockito.eq(hostMock), Mockito.eq(200L)); + Mockito.verify(storageManager, Mockito.times(1)).connectHostToSharedPool(Mockito.eq(hostMock), Mockito.eq(201L)); + Mockito.verify(storageManager, Mockito.times(1)).createCapacityEntry(Mockito.eq(200L)); + Mockito.verify(storageManager, Mockito.times(1)).createCapacityEntry(Mockito.eq(201L)); + } + } }