ConfigureStorageAccessCmd: Allow storage access configuration on empt… (#578) (#13551)
Some checks failed
Build / build (push) Has been cancelled
Simulator CI / build (component/find_hosts_for_migration component/test_acl_isolatednetwork component/test_acl_isolatednetwork_delete component/test_acl_listsnapshot) (push) Has been cancelled
Simulator CI / build (component/test_acl_listvm component/test_acl_listvolume) (push) Has been cancelled
Simulator CI / build (component/test_acl_sharednetwork component/test_acl_sharednetwork_deployVM-impersonation component/test_user_private_gateway component/test_user_shared_network) (push) Has been cancelled
Simulator CI / build (component/test_affinity_groups_projects component/test_allocation_states component/test_assign_vm) (push) Has been cancelled
Simulator CI / build (component/test_concurrent_snapshots_limit component/test_cpu_domain_limits component/test_cpu_limits component/test_cpu_max_limits component/test_cpu_project_limits component/test_deploy_vm_userdata_multi_nic component/test_deploy_vm_lease) (push) Has been cancelled
Simulator CI / build (component/test_egress_fw_rules component/test_invalid_gw_nm component/test_ip_reservation) (push) Has been cancelled
Simulator CI / build (component/test_lb_secondary_ip component/test_list_nics component/test_list_pod component/test_memory_limits) (push) Has been cancelled
Simulator CI / build (component/test_mm_domain_limits component/test_mm_max_limits component/test_mm_project_limits component/test_network_offering component/test_non_contiguous_vlan) (push) Has been cancelled
Simulator CI / build (component/test_persistent_networks component/test_project_configs component/test_project_limits component/test_project_resources) (push) Has been cancelled
Simulator CI / build (component/test_project_usage component/test_protocol_number_security_group component/test_public_ip component/test_resource_limits component/test_resource_limit_tags) (push) Has been cancelled
Simulator CI / build (component/test_regions_accounts component/test_routers component/test_snapshots component/test_stopped_vm component/test_tags component/test_templates component/test_updateResourceCount component/test_update_vm) (push) Has been cancelled
Simulator CI / build (component/test_volumes component/test_vpc component/test_vpc_distributed_routing_offering component/test_vpc_network component/test_vpc_offerings component/test_vpc_routers component/test_vpn_users component/test_vpc_network_lbrules) (push) Has been cancelled
Simulator CI / build (smoke/test_accounts smoke/test_account_access smoke/test_affinity_groups smoke/test_affinity_groups_projects smoke/test_annotations smoke/test_async_job smoke/test_attach_multiple_volumes smoke/test_backup_recovery_dummy smoke/test_certauthority… (push) Has been cancelled
Simulator CI / build (smoke/test_cluster_drs smoke/test_dynamicroles smoke/test_enable_account_settings_for_domain smoke/test_enable_role_based_users_in_projects smoke/test_events_resource smoke/test_global_settings smoke/test_guest_vlan_range smoke/test_host_mainten… (push) Has been cancelled
Simulator CI / build (smoke/test_list_accounts smoke/test_list_disk_offerings smoke/test_list_domains smoke/test_list_hosts smoke/test_list_service_offerings smoke/test_list_storage_pools smoke/test_list_volumes) (push) Has been cancelled
Simulator CI / build (smoke/test_network smoke/test_network_acl smoke/test_network_ipv6 smoke/test_network_permissions smoke/test_nic smoke/test_nic_adapter_type smoke/test_non_contigiousvlan smoke/test_object_stores smoke/test_outofbandmanagement smoke/test_outofban… (push) Has been cancelled
Simulator CI / build (smoke/test_router_dhcphosts smoke/test_router_dns smoke/test_router_dnsservice smoke/test_routers smoke/test_routers_iptables_default_policy smoke/test_routers_network_ops smoke/test_scale_vm smoke/test_secondary_storage smoke/test_service_offer… (push) Has been cancelled
Coverage Check / codecov (push) Has been cancelled
PR Merge Conflict Check / triage (push) Has been cancelled
License Check / build (push) Has been cancelled
UI Build / build (push) Has been cancelled

* 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 <32716246+sachindoddaguni@users.noreply.github.com>
Co-authored-by: Sachin R Doddaguni <s_rudrappadoddagu@apple.com>
Co-authored-by: mprokopchuk <mprokopchuk@gmail.com>
This commit is contained in:
Nicolas Vazquez 2026-07-14 20:48:52 -03:00 committed by GitHub
parent 17e5947a6d
commit f4c0c312f6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 98 additions and 3 deletions

View File

@ -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);

View File

@ -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);
}
}