Skip to content

Commit eba9053

Browse files
nvazquezdhslove
authored andcommitted
Find system VM templates for CKS clusters and SharedFS honouring the preferred architecture (apache#10946)
* Find system VM templates for CKS cluster honouring the preferred architecture * Fix unit tests * Fix checkstyle * Sort instead of filtering by preferred arch * Remove unnecesary stubs * Restore java version * Address review comments * Fail and display error message in case the CKS ISO arch doesnt match the selected template arch * Prefer CKS ISO arch instead of the system VM setting
1 parent 71f6e52 commit eba9053

File tree

9 files changed

+70
-236
lines changed

9 files changed

+70
-236
lines changed

engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDao.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public interface VMTemplateDao extends GenericDao<VMTemplateVO, Long>, StateDao<
7474

7575
VMTemplateVO findSystemVMTemplate(long zoneId);
7676

77-
VMTemplateVO findSystemVMReadyTemplate(long zoneId, HypervisorType hypervisorType);
77+
VMTemplateVO findSystemVMReadyTemplate(long zoneId, HypervisorType hypervisorType, String preferredArch);
7878

7979
List<VMTemplateVO> findSystemVMReadyTemplates(long zoneId, HypervisorType hypervisorType, String preferredArch);
8080

engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.HashMap;
2525
import java.util.List;
2626
import java.util.Map;
27+
import java.util.stream.Collectors;
2728

2829
import javax.inject.Inject;
2930
import javax.naming.ConfigurationException;
@@ -621,11 +622,19 @@ public List<VMTemplateVO> listAllReadySystemVMTemplates(Long zoneId) {
621622
}
622623

623624
@Override
624-
public VMTemplateVO findSystemVMReadyTemplate(long zoneId, HypervisorType hypervisorType) {
625+
public VMTemplateVO findSystemVMReadyTemplate(long zoneId, HypervisorType hypervisorType, String preferredArch) {
625626
List<VMTemplateVO> templates = listAllReadySystemVMTemplates(zoneId);
626627
if (CollectionUtils.isEmpty(templates)) {
627628
return null;
628629
}
630+
if (StringUtils.isNotBlank(preferredArch)) {
631+
// Sort the templates by preferred architecture first
632+
templates = templates.stream()
633+
.sorted(Comparator.comparing(
634+
x -> !x.getArch().getType().equalsIgnoreCase(preferredArch)
635+
))
636+
.collect(Collectors.toList());
637+
}
629638
if (hypervisorType == HypervisorType.Any) {
630639
return templates.get(0);
631640
}

engine/schema/src/test/java/com/cloud/storage/dao/VMTemplateDaoImplTest.java

Lines changed: 18 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import java.util.List;
3737
import java.util.Map;
3838

39+
import org.junit.Assert;
3940
import org.junit.Test;
4041
import org.junit.runner.RunWith;
4142
import org.mockito.InjectMocks;
@@ -198,122 +199,23 @@ public void testFindLatestTemplateByTypeAndHypervisorAndArch_NotFound() {
198199
assertNull(result);
199200
}
200201

201-
private void mockTemplateZoneJoin() {
202-
VMTemplateZoneVO templateZoneVO = mock(VMTemplateZoneVO.class);
203-
SearchBuilder<VMTemplateZoneVO> templateZoneVOSearchBuilder = mock(SearchBuilder.class);
204-
when(templateZoneVOSearchBuilder.entity()).thenReturn(templateZoneVO);
205-
when(templateZoneDao.createSearchBuilder()).thenReturn(templateZoneVOSearchBuilder);
206-
}
207-
208-
@Test
209-
public void testListTemplateIsoByArchAndZone_WithDataCenterId() {
210-
Long dataCenterId = 1L;
211-
CPU.CPUArch arch = CPU.CPUArch.getDefault();
212-
Boolean isIso = true;
213-
VMTemplateVO templateVO = mock(VMTemplateVO.class);
214-
GenericSearchBuilder<VMTemplateVO, Long> searchBuilder = mock(GenericSearchBuilder.class);
215-
when(searchBuilder.entity()).thenReturn(templateVO);
216-
SearchCriteria<Long>searchCriteria = mock(SearchCriteria.class);
217-
when(templateDao.createSearchBuilder(Long.class)).thenReturn(searchBuilder);
218-
when(searchBuilder.create()).thenReturn(searchCriteria);
219-
mockTemplateZoneJoin();
220-
doReturn(new ArrayList<>()).when(templateDao).customSearch(searchCriteria, null);
221-
List<Long> result = templateDao.listTemplateIsoByArchVnfAndZone(dataCenterId, arch, isIso, false);
222-
assertNotNull(result);
223-
verify(searchBuilder, times(1)).select(null, SearchCriteria.Func.DISTINCT, templateVO.getGuestOSId());
224-
verify(searchBuilder, times(1)).and(eq("state"), any(), eq(SearchCriteria.Op.IN));
225-
verify(searchBuilder, times(1)).and(eq("type"), any(), eq(SearchCriteria.Op.IN));
226-
verify(searchBuilder, times(1)).and(eq("arch"), any(), eq(SearchCriteria.Op.EQ));
227-
verify(searchBuilder, times(1)).and(eq("isIso"), any(), eq(SearchCriteria.Op.EQ));
228-
verify(searchBuilder, times(1)).join(eq("templateZoneSearch"), any(), any(), any(), eq(JoinBuilder.JoinType.INNER));
229-
verify(templateDao, times(1)).customSearch(searchCriteria, null);
230-
}
231-
232202
@Test
233-
public void testListTemplateIsoByArchAndZone_WithoutDataCenterId() {
234-
Long dataCenterId = null;
235-
CPU.CPUArch arch = CPU.CPUArch.getDefault();
236-
Boolean isIso = false;
237-
VMTemplateVO templateVO = mock(VMTemplateVO.class);
238-
GenericSearchBuilder<VMTemplateVO, Long> searchBuilder = mock(GenericSearchBuilder.class);
239-
when(searchBuilder.entity()).thenReturn(templateVO);
240-
SearchCriteria<Long>searchCriteria = mock(SearchCriteria.class);
241-
when(templateDao.createSearchBuilder(Long.class)).thenReturn(searchBuilder);
242-
when(searchBuilder.create()).thenReturn(searchCriteria);
243-
doReturn(new ArrayList<>()).when(templateDao).customSearch(searchCriteria, null);
244-
List<Long> result = templateDao.listTemplateIsoByArchVnfAndZone(dataCenterId, arch, isIso, false);
245-
assertNotNull(result);
246-
verify(searchBuilder, times(1)).select(null, SearchCriteria.Func.DISTINCT, templateVO.getGuestOSId());
247-
verify(searchBuilder, times(1)).and(eq("state"), any(), eq(SearchCriteria.Op.IN));
248-
verify(searchBuilder, times(1)).and(eq("type"), any(), eq(SearchCriteria.Op.IN));
249-
verify(searchBuilder, times(1)).and(eq("arch"), any(), eq(SearchCriteria.Op.EQ));
250-
verify(searchBuilder, times(1)).and(eq("isIso"), any(), eq(SearchCriteria.Op.NEQ));
251-
verify(searchBuilder, never()).join(eq("templateZoneSearch"), any(), any(), any(), eq(JoinBuilder.JoinType.INNER));
252-
verify(templateDao, times(1)).customSearch(searchCriteria, null);
253-
}
254-
255-
@Test
256-
public void testListTemplateIsoByArchAndZone_WithoutArch() {
257-
Long dataCenterId = 1L;
258-
CPU.CPUArch arch = null;
259-
Boolean isIso = true;
260-
VMTemplateVO templateVO = mock(VMTemplateVO.class);
261-
GenericSearchBuilder<VMTemplateVO, Long> searchBuilder = mock(GenericSearchBuilder.class);
262-
when(searchBuilder.entity()).thenReturn(templateVO);
263-
SearchCriteria<Long>searchCriteria = mock(SearchCriteria.class);
264-
when(templateDao.createSearchBuilder(Long.class)).thenReturn(searchBuilder);
265-
when(searchBuilder.create()).thenReturn(searchCriteria);
266-
mockTemplateZoneJoin();
267-
doReturn(new ArrayList<>()).when(templateDao).customSearch(searchCriteria, null);
268-
List<Long> result = templateDao.listTemplateIsoByArchVnfAndZone(dataCenterId, arch, isIso, false);
269-
assertNotNull(result);
270-
verify(searchBuilder, times(1)).select(null, SearchCriteria.Func.DISTINCT, templateVO.getGuestOSId());
271-
verify(searchBuilder, times(1)).and(eq("state"), any(), eq(SearchCriteria.Op.IN));
272-
verify(searchBuilder, times(1)).and(eq("type"), any(), eq(SearchCriteria.Op.IN));
273-
verify(searchBuilder, times(1)).and(eq("arch"), any(), eq(SearchCriteria.Op.EQ));
274-
verify(searchBuilder, times(1)).and(eq("isIso"), any(), eq(SearchCriteria.Op.EQ));
275-
verify(searchBuilder, times(1)).join(eq("templateZoneSearch"), any(), any(), any(), eq(JoinBuilder.JoinType.INNER));
276-
verify(templateDao, times(1)).customSearch(searchCriteria, null);
277-
}
278-
279-
@Test
280-
public void testListTemplateIsoByArchAndZone_WithoutIsIso() {
281-
Long dataCenterId = 1L;
282-
CPU.CPUArch arch = CPU.CPUArch.getDefault();
283-
Boolean isIso = null;
284-
VMTemplateVO templateVO = mock(VMTemplateVO.class);
285-
GenericSearchBuilder<VMTemplateVO, Long> searchBuilder = mock(GenericSearchBuilder.class);
286-
when(searchBuilder.entity()).thenReturn(templateVO);
287-
SearchCriteria<Long>searchCriteria = mock(SearchCriteria.class);
288-
when(templateDao.createSearchBuilder(Long.class)).thenReturn(searchBuilder);
289-
when(searchBuilder.create()).thenReturn(searchCriteria);
290-
mockTemplateZoneJoin();
291-
doReturn(new ArrayList<>()).when(templateDao).customSearch(searchCriteria, null);
292-
List<Long> result = templateDao.listTemplateIsoByArchVnfAndZone(dataCenterId, arch, isIso, false);
293-
assertNotNull(result);
294-
verify(searchBuilder, times(1)).select(null, SearchCriteria.Func.DISTINCT, templateVO.getGuestOSId());
295-
verify(searchBuilder, times(1)).and(eq("state"), any(), eq(SearchCriteria.Op.IN));
296-
verify(searchBuilder, times(1)).and(eq("type"), any(), eq(SearchCriteria.Op.IN));
297-
verify(searchBuilder, times(1)).and(eq("arch"), any(), eq(SearchCriteria.Op.EQ));
298-
verify(searchBuilder, never()).and(eq("isIso"), any(), eq(SearchCriteria.Op.NEQ));
299-
verify(searchBuilder, never()).and(eq("isIso"), any(), eq(SearchCriteria.Op.EQ));
300-
verify(searchBuilder, times(1)).join(eq("templateZoneSearch"), any(), any(), any(), eq(JoinBuilder.JoinType.INNER));
301-
verify(templateDao, times(1)).customSearch(searchCriteria, null);
302-
}
303-
304-
@Test
305-
public void testListIdsByExtensionId_ReturnsIds() {
306-
long extensionId = 42L;
307-
List<Long> expectedIds = Arrays.asList(1L, 2L, 3L);
308-
GenericSearchBuilder<VMTemplateVO, Long> searchBuilder = mock(GenericSearchBuilder.class);
309-
SearchCriteria<Long> searchCriteria = mock(SearchCriteria.class);
310-
when(templateDao.createSearchBuilder(Long.class)).thenReturn(searchBuilder);
311-
when(searchBuilder.entity()).thenReturn(mock(VMTemplateVO.class));
312-
when(searchBuilder.create()).thenReturn(searchCriteria);
313-
doReturn(expectedIds).when(templateDao).customSearchIncludingRemoved(eq(searchCriteria), isNull());
314-
List<Long> result = templateDao.listIdsByExtensionId(extensionId);
315-
assertEquals(expectedIds, result);
316-
verify(searchCriteria).setParameters("extensionId", extensionId);
317-
verify(templateDao).customSearchIncludingRemoved(eq(searchCriteria), isNull());
203+
public void testFindSystemVMReadyTemplate() {
204+
Long zoneId = 1L;
205+
VMTemplateVO systemVmTemplate1 = mock(VMTemplateVO.class);
206+
Mockito.when(systemVmTemplate1.getArch()).thenReturn(CPU.CPUArch.x86);
207+
VMTemplateVO systemVmTemplate2 = mock(VMTemplateVO.class);
208+
Mockito.when(systemVmTemplate2.getArch()).thenReturn(CPU.CPUArch.x86);
209+
VMTemplateVO systemVmTemplate3 = mock(VMTemplateVO.class);
210+
Mockito.when(systemVmTemplate3.getArch()).thenReturn(CPU.CPUArch.arm64);
211+
Mockito.when(systemVmTemplate3.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
212+
List<VMTemplateVO> templates = Arrays.asList(systemVmTemplate1, systemVmTemplate2, systemVmTemplate3);
213+
Mockito.when(hostDao.listDistinctHypervisorTypes(zoneId)).thenReturn(Arrays.asList(Hypervisor.HypervisorType.KVM));
214+
SearchBuilder<VMTemplateVO> sb = mock(SearchBuilder.class);
215+
templateDao.readySystemTemplateSearch = sb;
216+
when(sb.create()).thenReturn(mock(SearchCriteria.class));
217+
doReturn(templates).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class));
218+
VMTemplateVO readyTemplate = templateDao.findSystemVMReadyTemplate(zoneId, Hypervisor.HypervisorType.KVM, CPU.CPUArch.arm64.getType());
219+
Assert.assertEquals(CPU.CPUArch.arm64, readyTemplate.getArch());
318220
}
319221
}

plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -487,8 +487,14 @@ private IpAddress getSourceNatIp(Network network) {
487487
return null;
488488
}
489489

490-
public VMTemplateVO getKubernetesServiceTemplate(DataCenter dataCenter, Hypervisor.HypervisorType hypervisorType, Map<String, Long> templateNodeTypeMap, KubernetesClusterNodeType nodeType) {
491-
VMTemplateVO template = templateDao.findSystemVMReadyTemplate(dataCenter.getId(), hypervisorType);
490+
public VMTemplateVO getKubernetesServiceTemplate(DataCenter dataCenter, Hypervisor.HypervisorType hypervisorType, Map<String, Long> templateNodeTypeMap, KubernetesClusterNodeType nodeType,
491+
KubernetesSupportedVersion clusterKubernetesVersion) {
492+
String systemVMPreferredArchitecture = ResourceManager.SystemVmPreferredArchitecture.valueIn(dataCenter.getId());
493+
VMTemplateVO cksIso = clusterKubernetesVersion != null ?
494+
templateDao.findById(clusterKubernetesVersion.getIsoId()) :
495+
null;
496+
String preferredArchitecture = getCksClusterPreferredArch(systemVMPreferredArchitecture, cksIso);
497+
VMTemplateVO template = templateDao.findSystemVMReadyTemplate(dataCenter.getId(), hypervisorType, preferredArchitecture);
492498
if (DataCenter.Type.Edge.equals(dataCenter.getType()) && template != null && !template.isDirectDownload()) {
493499
logger.debug(String.format("Template %s can not be used for edge zone %s", template, dataCenter));
494500
template = templateDao.findRoutingTemplate(hypervisorType, networkHelper.getHypervisorRouterTemplateConfigMap().get(hypervisorType).valueIn(dataCenter.getId()));
@@ -515,6 +521,14 @@ public void throwDefaultCksTemplateNotFound(String datacenterId) {
515521
throw new CloudRuntimeException("Not able to find the System or Routing template in ready state for the zone " + datacenterId);
516522
}
517523

524+
protected String getCksClusterPreferredArch(String systemVMPreferredArchitecture, VMTemplateVO cksIso) {
525+
if (cksIso == null) {
526+
return systemVMPreferredArchitecture;
527+
}
528+
String cksIsoArchName = cksIso.getArch().name();
529+
return cksIsoArchName.equals(systemVMPreferredArchitecture) ? systemVMPreferredArchitecture : cksIsoArchName;
530+
}
531+
518532
protected void validateIsolatedNetworkIpRules(long ipId, FirewallRule.Purpose purpose, Network network, int clusterTotalNodeCount) {
519533
List<FirewallRuleVO> rules = firewallRulesDao.listByIpPurposeProtocolAndNotRevoked(ipId, purpose, NetUtils.TCP_PROTO);
520534
for (FirewallRuleVO rule : rules) {
@@ -1547,11 +1561,12 @@ public KubernetesCluster createManagedKubernetesCluster(CreateKubernetesClusterC
15471561
}
15481562

15491563
Map<String, Long> templateNodeTypeMap = cmd.getTemplateNodeTypeMap();
1550-
final VMTemplateVO finalTemplate = getKubernetesServiceTemplate(zone, hypervisorType, templateNodeTypeMap, DEFAULT);
1551-
final VMTemplateVO controlNodeTemplate = getKubernetesServiceTemplate(zone, hypervisorType, templateNodeTypeMap, CONTROL);
1552-
final VMTemplateVO workerNodeTemplate = getKubernetesServiceTemplate(zone, hypervisorType, templateNodeTypeMap, WORKER);
1553-
final VMTemplateVO etcdNodeTemplate = getKubernetesServiceTemplate(zone, hypervisorType, templateNodeTypeMap, ETCD);
1564+
final VMTemplateVO finalTemplate = getKubernetesServiceTemplate(zone, hypervisorType, templateNodeTypeMap, DEFAULT, clusterKubernetesVersion);
1565+
final VMTemplateVO controlNodeTemplate = getKubernetesServiceTemplate(zone, hypervisorType, templateNodeTypeMap, CONTROL, clusterKubernetesVersion);
1566+
final VMTemplateVO workerNodeTemplate = getKubernetesServiceTemplate(zone, hypervisorType, templateNodeTypeMap, WORKER, clusterKubernetesVersion);
1567+
final VMTemplateVO etcdNodeTemplate = getKubernetesServiceTemplate(zone, hypervisorType, templateNodeTypeMap, ETCD, clusterKubernetesVersion);
15541568
final Network defaultNetwork = getKubernetesClusterNetworkIfMissing(cmd.getName(), zone, owner, (int)controlNodeCount, (int)clusterSize, cmd.getExternalLoadBalancerIpAddress(), cmd.getNetworkId(), asNumber);
1569+
15551570
final SecurityGroup finalSecurityGroup = securityGroup;
15561571
final KubernetesClusterVO cluster = Transaction.execute(new TransactionCallback<KubernetesClusterVO>() {
15571572
@Override

plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838

3939
import javax.inject.Inject;
4040

41+
import com.cloud.kubernetes.version.KubernetesSupportedVersionVO;
4142
import org.apache.logging.log4j.Level;
4243
import org.apache.logging.log4j.Logger;
4344
import org.apache.logging.log4j.LogManager;
@@ -259,7 +260,8 @@ protected void init() {
259260
DataCenterVO dataCenterVO = dataCenterDao.findById(zoneId);
260261
VMTemplateVO template = templateDao.findById(templateId);
261262
Hypervisor.HypervisorType type = template.getHypervisorType();
262-
this.clusterTemplate = manager.getKubernetesServiceTemplate(dataCenterVO, type, null, KubernetesClusterNodeType.DEFAULT);
263+
KubernetesSupportedVersionVO kubernetesSupportedVersion = kubernetesSupportedVersionDao.findById(this.kubernetesCluster.getKubernetesVersionId());
264+
this.clusterTemplate = manager.getKubernetesServiceTemplate(dataCenterVO, type, null, KubernetesClusterNodeType.DEFAULT, kubernetesSupportedVersion);
263265
this.controlNodeTemplate = templateDao.findById(this.kubernetesCluster.getControlNodeTemplateId());
264266
this.workerNodeTemplate = templateDao.findById(this.kubernetesCluster.getWorkerNodeTemplateId());
265267
this.etcdTemplate = templateDao.findById(this.kubernetesCluster.getEtcdNodeTemplateId());

plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ protected DeployDestination plan(final long nodesCount, final DataCenter zone, f
212212
for (Map.Entry<String, Pair<HostVO, Integer>> hostEntry : hosts_with_resevered_capacity.entrySet()) {
213213
Pair<HostVO, Integer> hp = hostEntry.getValue();
214214
HostVO h = hp.first();
215-
if (!h.getHypervisorType().equals(clusterTemplate.getHypervisorType())) {
215+
if (!h.getHypervisorType().equals(clusterTemplate.getHypervisorType()) || !h.getArch().equals(clusterTemplate.getArch())) {
216216
continue;
217217
}
218218
hostDao.loadHostTags(h);

0 commit comments

Comments
 (0)