Skip to content

Commit e1cf0f3

Browse files
committed
Merge branch 4.22 to main
* 4.22: UI: fix blank page when list Other OSes in VNF appliance deployment (#13620) ConfigureStorageAccessCmd: Allow storage access configuration on empt… (#578) (#13551)
2 parents 3b53e8c + 0339f31 commit e1cf0f3

3 files changed

Lines changed: 99 additions & 4 deletions

File tree

server/src/main/java/com/cloud/resource/ResourceManagerImpl.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2511,7 +2511,8 @@ protected void checkIfAllHostsInUse(List<String> sagsToDelete, Long clusterId, L
25112511
List<HostVO> hostsInZone = _hostDao.findByDataCenterId(zoneId);
25122512
Set<Long> hostIdsInUseSet = hostIdsUsingStorageAccessGroups.stream().collect(Collectors.toSet());
25132513

2514-
boolean allInUseZone = hostsInZone.stream()
2514+
// allMatch returns true on empty stream, need to check whether collection is not empty first
2515+
boolean allInUseZone = !hostsInZone.isEmpty() && hostsInZone.stream()
25152516
.map(HostVO::getId)
25162517
.allMatch(hostIdsInUseSet::contains);
25172518

@@ -2525,7 +2526,8 @@ protected void checkIfAllHostsInUse(List<String> sagsToDelete, Long clusterId, L
25252526
List<HostVO> hostsInCluster = _hostDao.findByClusterId(clusterId, Type.Routing);
25262527
Set<Long> hostIdsInUseSet = hostIdsUsingStorageAccessGroups.stream().collect(Collectors.toSet());
25272528

2528-
boolean allInUseCluster = hostsInCluster.stream()
2529+
// allMatch returns true on empty stream, need to check whether collection is not empty first
2530+
boolean allInUseCluster = !hostsInCluster.isEmpty() && hostsInCluster.stream()
25292531
.map(HostVO::getId)
25302532
.allMatch(hostIdsInUseSet::contains);
25312533

@@ -2539,7 +2541,8 @@ protected void checkIfAllHostsInUse(List<String> sagsToDelete, Long clusterId, L
25392541
List<HostVO> hostsInPod = _hostDao.findByPodId(podId, Type.Routing);
25402542
Set<Long> hostIdsInUseSet = hostIdsUsingStorageAccessGroups.stream().collect(Collectors.toSet());
25412543

2542-
boolean allInUsePod = hostsInPod.stream()
2544+
// allMatch returns true on empty stream, need to check whether collection is not empty first
2545+
boolean allInUsePod = !hostsInPod.isEmpty() && hostsInPod.stream()
25432546
.map(HostVO::getId)
25442547
.allMatch(hostIdsInUseSet::contains);
25452548

server/src/test/java/com/cloud/resource/ResourceManagerImplTest.java

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,4 +1211,96 @@ public void executeUserRequestDefaultOverloadPassesFalseForDeleteHost() throws E
12111211

12121212
Mockito.verify(resourceManager).doDeleteHost(hostId, false, false);
12131213
}
1214+
1215+
@Test
1216+
public void testUpdateClusterStorageAccessGroupsWithEmptyHostsInCluster() {
1217+
Long clusterId = 1L;
1218+
List<String> newStorageAccessGroups = Arrays.asList("sag1", "sag2");
1219+
1220+
ClusterVO cluster = Mockito.mock(ClusterVO.class);
1221+
Mockito.when(cluster.getId()).thenReturn(clusterId);
1222+
Mockito.when(cluster.getStorageAccessGroups()).thenReturn("sag3,sag4"); // existing SAGs
1223+
Mockito.when(resourceManager.getCluster(clusterId)).thenReturn(cluster);
1224+
List<HostVO> emptyHostsList = new ArrayList<>();
1225+
Mockito.when(hostDao.findHypervisorHostInCluster(clusterId)).thenReturn(emptyHostsList);
1226+
Mockito.when(hostDao.findByClusterId(clusterId, Host.Type.Routing)).thenReturn(emptyHostsList);
1227+
List<Long> emptyHostIdsList = new ArrayList<>();
1228+
Mockito.doReturn(emptyHostIdsList).when(resourceManager)
1229+
.listOfHostIdsUsingTheStorageAccessGroups(Mockito.anyList(), eq(clusterId), eq(null), eq(null));
1230+
try {
1231+
resourceManager.updateClusterStorageAccessGroups(clusterId, newStorageAccessGroups);
1232+
} catch (CloudRuntimeException e) {
1233+
Assert.fail("updateClusterStorageAccessGroups should not throw CloudRuntimeException when cluster has no hosts. Error: " + e.getMessage());
1234+
}
1235+
Mockito.verify(resourceManager).checkIfAllHostsInUse(Mockito.anyList(), eq(clusterId), eq(null), eq(null));
1236+
}
1237+
1238+
@Test
1239+
public void testUpdateClusterStorageAccessGroupsWithEmptyHostsInZone() {
1240+
List<String> sagsToDelete = Arrays.asList("tag1", "tag2");
1241+
Long clusterId = null;
1242+
Long podId = null;
1243+
Long zoneId = 3L;
1244+
1245+
List<Long> emptyHostIdsList = new ArrayList<>();
1246+
Mockito.doReturn(emptyHostIdsList).when(resourceManager)
1247+
.listOfHostIdsUsingTheStorageAccessGroups(sagsToDelete, clusterId, podId, zoneId);
1248+
List<HostVO> emptyHostsInZone = new ArrayList<>();
1249+
Mockito.doReturn(emptyHostsInZone).when(hostDao).findByDataCenterId(zoneId);
1250+
1251+
try {
1252+
resourceManager.checkIfAllHostsInUse(sagsToDelete, clusterId, podId, zoneId);
1253+
} catch (CloudRuntimeException e) {
1254+
Assert.fail("checkIfAllHostsInUse should not throw CloudRuntimeException when zone has no hosts. Error: " + e.getMessage());
1255+
}
1256+
Mockito.verify(resourceManager).checkIfAllHostsInUse(Mockito.anyList(), eq(null), eq(null), eq(zoneId));
1257+
}
1258+
1259+
@Test
1260+
public void testUpdateClusterStorageAccessGroupsWithEmptyHostsInPod() {
1261+
List<String> sagsToDelete = Arrays.asList("tag1", "tag2");
1262+
Long clusterId = null;
1263+
Long podId = 2L;
1264+
Long zoneId = null;
1265+
1266+
List<Long> emptyHostIdsList = new ArrayList<>();
1267+
Mockito.doReturn(emptyHostIdsList).when(resourceManager)
1268+
.listOfHostIdsUsingTheStorageAccessGroups(sagsToDelete, clusterId, podId, zoneId);
1269+
List<HostVO> emptyHostsInPod = new ArrayList<>();
1270+
Mockito.doReturn(emptyHostsInPod).when(hostDao).findByPodId(podId, Host.Type.Routing);
1271+
1272+
try {
1273+
resourceManager.checkIfAllHostsInUse(sagsToDelete, clusterId, podId, zoneId);
1274+
} catch (CloudRuntimeException e) {
1275+
Assert.fail("checkIfAllHostsInUse should not throw CloudRuntimeException when pod has no hosts. Error: " + e.getMessage());
1276+
}
1277+
Mockito.verify(resourceManager).checkIfAllHostsInUse(Mockito.anyList(), eq(null), eq(podId), eq(null));
1278+
}
1279+
1280+
@Test
1281+
public void testCheckIfAllHostsInUseWithEmptyHostsInMultipleLevels() {
1282+
List<String> sagsToDelete = Arrays.asList("tag1", "tag2");
1283+
Long clusterId = 1L;
1284+
Long podId = 2L;
1285+
Long zoneId = 3L;
1286+
1287+
List<Long> emptyHostIdsList = new ArrayList<>();
1288+
Mockito.doReturn(emptyHostIdsList).when(resourceManager)
1289+
.listOfHostIdsUsingTheStorageAccessGroups(sagsToDelete, clusterId, podId, zoneId);
1290+
List<HostVO> emptyHostsInZone = new ArrayList<>();
1291+
List<HostVO> emptyHostsInCluster = new ArrayList<>();
1292+
List<HostVO> emptyHostsInPod = new ArrayList<>();
1293+
Mockito.doReturn(emptyHostsInZone).when(hostDao).findByDataCenterId(zoneId);
1294+
Mockito.doReturn(emptyHostsInCluster).when(hostDao).findByClusterId(clusterId, Host.Type.Routing);
1295+
Mockito.doReturn(emptyHostsInPod).when(hostDao).findByPodId(podId, Host.Type.Routing);
1296+
1297+
try {
1298+
resourceManager.checkIfAllHostsInUse(sagsToDelete, clusterId, podId, zoneId);
1299+
} catch (CloudRuntimeException e) {
1300+
Assert.fail("checkIfAllHostsInUse should not throw CloudRuntimeException when all levels have no hosts. Error: " + e.getMessage());
1301+
}
1302+
Mockito.verify(hostDao).findByDataCenterId(zoneId);
1303+
Mockito.verify(hostDao).findByClusterId(clusterId, Host.Type.Routing);
1304+
Mockito.verify(hostDao).findByPodId(podId, Host.Type.Routing);
1305+
}
12141306
}

ui/src/views/compute/DeployVnfAppliance.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@
272272
</template>
273273
</a-step>
274274
<a-step
275-
v-else-if="vm.templateid && template.templatetype !== 'VNF'"
275+
v-else-if="vm.templateid && template && template.templatetype !== 'VNF'"
276276
:title="imageType === 'templateid' ? $t('label.data.disk') : $t('label.disk.size')"
277277
:status="zoneSelected ? 'process' : 'wait'">
278278
<template #description>

0 commit comments

Comments
 (0)