Merge branch 4.22 to main
Some checks failed
CodeQL Analysis / CodeQL (actions) (push) Has been cancelled
Docker Image Build / build (push) Has been cancelled
Sonar Quality Check (Main) / Sonar JaCoCo Coverage (push) Has been cancelled

* 4.22:
  UI: fix blank page when list Other OSes in VNF appliance deployment (#13620)
  ConfigureStorageAccessCmd: Allow storage access configuration on empt… (#578) (#13551)
This commit is contained in:
Fabricio Duarte 2026-07-22 12:21:40 -03:00
commit e1cf0f335a
3 changed files with 99 additions and 4 deletions

View File

@ -2511,7 +2511,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);
@ -2525,7 +2526,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);
@ -2539,7 +2541,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);
}
}

View File

@ -272,7 +272,7 @@
</template>
</a-step>
<a-step
v-else-if="vm.templateid && template.templatetype !== 'VNF'"
v-else-if="vm.templateid && template && template.templatetype !== 'VNF'"
:title="imageType === 'templateid' ? $t('label.data.disk') : $t('label.disk.size')"
:status="zoneSelected ? 'process' : 'wait'">
<template #description>