This is an automated email from the ASF dual-hosted git repository.

nvazquez pushed a commit to branch 4.22
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/4.22 by this push:
     new f4c0c312f6b ConfigureStorageAccessCmd: Allow storage access 
configuration on empt… (#578) (#13551)
f4c0c312f6b is described below

commit f4c0c312f6b1d314009d8fd177fdf69d7e54ba04
Author: Nicolas Vazquez <[email protected]>
AuthorDate: Tue Jul 14 20:48:52 2026 -0300

    ConfigureStorageAccessCmd: Allow storage access configuration on empt… 
(#578) (#13551)
    
    * ConfigureStorageAccessCmd: Allow storage access configuration on empty 
clusters/pods/zones
    
    * update as per comments
    
    
    
    * update as per review comments
    
    
    
    * update as per comments
    
    
    
    ---------
    
    
    
    (cherry picked from commit 32736b9e262522912cf8755acabfb7400006252f)
    
    Co-authored-by: Sachin R <[email protected]>
    Co-authored-by: Sachin R Doddaguni <[email protected]>
    Co-authored-by: mprokopchuk <[email protected]>
---
 .../com/cloud/resource/ResourceManagerImpl.java    |  9 ++-
 .../cloud/resource/ResourceManagerImplTest.java    | 92 ++++++++++++++++++++++
 2 files changed, 98 insertions(+), 3 deletions(-)

diff --git a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java 
b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
index 50c4670fb51..95b508bca4f 100755
--- a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
+++ b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
@@ -2465,7 +2465,8 @@ public class ResourceManagerImpl extends ManagerBase 
implements ResourceManager,
             List<HostVO> hostsInZone = _hostDao.findByDataCenterId(zoneId);
             Set<Long> hostIdsInUseSet = 
hostIdsUsingStorageAccessGroups.stream().collect(Collectors.toSet());
 
-            boolean allInUseZone = hostsInZone.stream()
+            // allMatch returns true on empty stream, need to check whether 
collection is not empty first
+            boolean allInUseZone = !hostsInZone.isEmpty() && 
hostsInZone.stream()
                     .map(HostVO::getId)
                     .allMatch(hostIdsInUseSet::contains);
 
@@ -2479,7 +2480,8 @@ public class ResourceManagerImpl extends ManagerBase 
implements ResourceManager,
             List<HostVO> hostsInCluster = _hostDao.findByClusterId(clusterId, 
Type.Routing);
             Set<Long> hostIdsInUseSet = 
hostIdsUsingStorageAccessGroups.stream().collect(Collectors.toSet());
 
-            boolean allInUseCluster = hostsInCluster.stream()
+            // allMatch returns true on empty stream, need to check whether 
collection is not empty first
+            boolean allInUseCluster = !hostsInCluster.isEmpty() && 
hostsInCluster.stream()
                     .map(HostVO::getId)
                     .allMatch(hostIdsInUseSet::contains);
 
@@ -2493,7 +2495,8 @@ public class ResourceManagerImpl extends ManagerBase 
implements ResourceManager,
             List<HostVO> hostsInPod = _hostDao.findByPodId(podId, 
Type.Routing);
             Set<Long> hostIdsInUseSet = 
hostIdsUsingStorageAccessGroups.stream().collect(Collectors.toSet());
 
-            boolean allInUsePod = hostsInPod.stream()
+            // allMatch returns true on empty stream, need to check whether 
collection is not empty first
+            boolean allInUsePod = !hostsInPod.isEmpty() && hostsInPod.stream()
                     .map(HostVO::getId)
                     .allMatch(hostIdsInUseSet::contains);
 
diff --git 
a/server/src/test/java/com/cloud/resource/ResourceManagerImplTest.java 
b/server/src/test/java/com/cloud/resource/ResourceManagerImplTest.java
index 30a021591a5..d644b036949 100644
--- a/server/src/test/java/com/cloud/resource/ResourceManagerImplTest.java
+++ b/server/src/test/java/com/cloud/resource/ResourceManagerImplTest.java
@@ -1211,4 +1211,96 @@ public class ResourceManagerImplTest {
 
         Mockito.verify(resourceManager).doDeleteHost(hostId, false, false);
     }
+
+    @Test
+    public void testUpdateClusterStorageAccessGroupsWithEmptyHostsInCluster() {
+        Long clusterId = 1L;
+        List<String> newStorageAccessGroups = Arrays.asList("sag1", "sag2");
+
+        ClusterVO cluster = Mockito.mock(ClusterVO.class);
+        Mockito.when(cluster.getId()).thenReturn(clusterId);
+        
Mockito.when(cluster.getStorageAccessGroups()).thenReturn("sag3,sag4"); // 
existing SAGs
+        
Mockito.when(resourceManager.getCluster(clusterId)).thenReturn(cluster);
+        List<HostVO> emptyHostsList = new ArrayList<>();
+        
Mockito.when(hostDao.findHypervisorHostInCluster(clusterId)).thenReturn(emptyHostsList);
+        Mockito.when(hostDao.findByClusterId(clusterId, 
Host.Type.Routing)).thenReturn(emptyHostsList);
+        List<Long> emptyHostIdsList = new ArrayList<>();
+        Mockito.doReturn(emptyHostIdsList).when(resourceManager)
+                .listOfHostIdsUsingTheStorageAccessGroups(Mockito.anyList(), 
eq(clusterId), eq(null), eq(null));
+        try {
+            resourceManager.updateClusterStorageAccessGroups(clusterId, 
newStorageAccessGroups);
+        } catch (CloudRuntimeException e) {
+            Assert.fail("updateClusterStorageAccessGroups should not throw 
CloudRuntimeException when cluster has no hosts. Error: " + e.getMessage());
+        }
+        
Mockito.verify(resourceManager).checkIfAllHostsInUse(Mockito.anyList(), 
eq(clusterId), eq(null), eq(null));
+    }
+
+    @Test
+    public void testUpdateClusterStorageAccessGroupsWithEmptyHostsInZone() {
+        List<String> sagsToDelete = Arrays.asList("tag1", "tag2");
+        Long clusterId = null;
+        Long podId = null;
+        Long zoneId = 3L;
+
+        List<Long> emptyHostIdsList = new ArrayList<>();
+        Mockito.doReturn(emptyHostIdsList).when(resourceManager)
+                .listOfHostIdsUsingTheStorageAccessGroups(sagsToDelete, 
clusterId, podId, zoneId);
+        List<HostVO> emptyHostsInZone = new ArrayList<>();
+        
Mockito.doReturn(emptyHostsInZone).when(hostDao).findByDataCenterId(zoneId);
+
+        try {
+            resourceManager.checkIfAllHostsInUse(sagsToDelete, clusterId, 
podId, zoneId);
+        } catch (CloudRuntimeException e) {
+            Assert.fail("checkIfAllHostsInUse should not throw 
CloudRuntimeException when zone has no hosts. Error: " + e.getMessage());
+        }
+        
Mockito.verify(resourceManager).checkIfAllHostsInUse(Mockito.anyList(), 
eq(null), eq(null), eq(zoneId));
+    }
+
+    @Test
+    public void testUpdateClusterStorageAccessGroupsWithEmptyHostsInPod() {
+        List<String> sagsToDelete = Arrays.asList("tag1", "tag2");
+        Long clusterId = null;
+        Long podId = 2L;
+        Long zoneId = null;
+
+        List<Long> emptyHostIdsList = new ArrayList<>();
+        Mockito.doReturn(emptyHostIdsList).when(resourceManager)
+                .listOfHostIdsUsingTheStorageAccessGroups(sagsToDelete, 
clusterId, podId, zoneId);
+        List<HostVO> emptyHostsInPod = new ArrayList<>();
+        Mockito.doReturn(emptyHostsInPod).when(hostDao).findByPodId(podId, 
Host.Type.Routing);
+
+        try {
+            resourceManager.checkIfAllHostsInUse(sagsToDelete, clusterId, 
podId, zoneId);
+        } catch (CloudRuntimeException e) {
+            Assert.fail("checkIfAllHostsInUse should not throw 
CloudRuntimeException when pod has no hosts. Error: " + e.getMessage());
+        }
+        
Mockito.verify(resourceManager).checkIfAllHostsInUse(Mockito.anyList(), 
eq(null), eq(podId), eq(null));
+    }
+
+    @Test
+    public void testCheckIfAllHostsInUseWithEmptyHostsInMultipleLevels() {
+        List<String> sagsToDelete = Arrays.asList("tag1", "tag2");
+        Long clusterId = 1L;
+        Long podId = 2L;
+        Long zoneId = 3L;
+
+        List<Long> emptyHostIdsList = new ArrayList<>();
+        Mockito.doReturn(emptyHostIdsList).when(resourceManager)
+                .listOfHostIdsUsingTheStorageAccessGroups(sagsToDelete, 
clusterId, podId, zoneId);
+        List<HostVO> emptyHostsInZone = new ArrayList<>();
+        List<HostVO> emptyHostsInCluster = new ArrayList<>();
+        List<HostVO> emptyHostsInPod = new ArrayList<>();
+        
Mockito.doReturn(emptyHostsInZone).when(hostDao).findByDataCenterId(zoneId);
+        
Mockito.doReturn(emptyHostsInCluster).when(hostDao).findByClusterId(clusterId, 
Host.Type.Routing);
+        Mockito.doReturn(emptyHostsInPod).when(hostDao).findByPodId(podId, 
Host.Type.Routing);
+
+        try {
+            resourceManager.checkIfAllHostsInUse(sagsToDelete, clusterId, 
podId, zoneId);
+        } catch (CloudRuntimeException e) {
+            Assert.fail("checkIfAllHostsInUse should not throw 
CloudRuntimeException when all levels have no hosts. Error: " + e.getMessage());
+        }
+        Mockito.verify(hostDao).findByDataCenterId(zoneId);
+        Mockito.verify(hostDao).findByClusterId(clusterId, Host.Type.Routing);
+        Mockito.verify(hostDao).findByPodId(podId, Host.Type.Routing);
+    }
 }

Reply via email to