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

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


The following commit(s) were added to refs/heads/4.20 by this push:
     new ed0d606e983 Find system VM templates for CKS clusters and SharedFS 
honouring the preferred architecture (#10946)
ed0d606e983 is described below

commit ed0d606e983cd5ae2e4cd5b7428d054b9768b6ca
Author: Nicolas Vazquez <nicovazque...@gmail.com>
AuthorDate: Thu Jul 31 08:12:47 2025 -0300

    Find system VM templates for CKS clusters and SharedFS honouring the 
preferred architecture (#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
---
 .../java/com/cloud/storage/dao/VMTemplateDao.java  |  2 +-
 .../com/cloud/storage/dao/VMTemplateDaoImpl.java   | 11 ++++++-
 .../cloud/storage/dao/VMTemplateDaoImplTest.java   | 21 ++++++++++++
 .../cluster/KubernetesClusterManagerImpl.java      | 38 ++++++++++++++++++++--
 .../KubernetesClusterActionWorker.java             |  4 ++-
 ...ernetesClusterResourceModifierActionWorker.java |  2 +-
 .../cluster/KubernetesClusterManagerImplTest.java  | 19 +++++++++++
 .../lifecycle/StorageVmSharedFSLifeCycle.java      |  3 +-
 .../lifecycle/StorageVmSharedFSLifeCycleTest.java  |  3 +-
 9 files changed, 93 insertions(+), 10 deletions(-)

diff --git 
a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDao.java 
b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDao.java
index 0b40366a866..c751f81f927 100644
--- a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDao.java
+++ b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDao.java
@@ -72,7 +72,7 @@ public interface VMTemplateDao extends 
GenericDao<VMTemplateVO, Long>, StateDao<
 
     VMTemplateVO findSystemVMTemplate(long zoneId);
 
-    VMTemplateVO findSystemVMReadyTemplate(long zoneId, HypervisorType 
hypervisorType);
+    VMTemplateVO findSystemVMReadyTemplate(long zoneId, HypervisorType 
hypervisorType, String preferredArch);
 
     List<VMTemplateVO> findSystemVMReadyTemplates(long zoneId, HypervisorType 
hypervisorType, String preferredArch);
 
diff --git 
a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java 
b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java
index 12c00a3209a..b6796cf8f9d 100644
--- a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java
@@ -23,6 +23,7 @@ import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
@@ -578,11 +579,19 @@ public class VMTemplateDaoImpl extends 
GenericDaoBase<VMTemplateVO, Long> implem
     }
 
     @Override
-    public VMTemplateVO findSystemVMReadyTemplate(long zoneId, HypervisorType 
hypervisorType) {
+    public VMTemplateVO findSystemVMReadyTemplate(long zoneId, HypervisorType 
hypervisorType, String preferredArch) {
         List<VMTemplateVO> templates = listAllReadySystemVMTemplates(zoneId);
         if (CollectionUtils.isEmpty(templates)) {
             return null;
         }
+        if (StringUtils.isNotBlank(preferredArch)) {
+            // Sort the templates by preferred architecture first
+            templates = templates.stream()
+                    .sorted(Comparator.comparing(
+                            x -> 
!x.getArch().getType().equalsIgnoreCase(preferredArch)
+                    ))
+                    .collect(Collectors.toList());
+        }
         if (hypervisorType == HypervisorType.Any) {
             return templates.get(0);
         }
diff --git 
a/engine/schema/src/test/java/com/cloud/storage/dao/VMTemplateDaoImplTest.java 
b/engine/schema/src/test/java/com/cloud/storage/dao/VMTemplateDaoImplTest.java
index df0b36ebdbf..e97a887cc47 100644
--- 
a/engine/schema/src/test/java/com/cloud/storage/dao/VMTemplateDaoImplTest.java
+++ 
b/engine/schema/src/test/java/com/cloud/storage/dao/VMTemplateDaoImplTest.java
@@ -31,6 +31,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.InjectMocks;
@@ -186,4 +187,24 @@ public class VMTemplateDaoImplTest {
         VMTemplateVO result = 
templateDao.findLatestTemplateByTypeAndHypervisorAndArch(hypervisorType, arch, 
type);
         assertNull(result);
     }
+
+    @Test
+    public void testFindSystemVMReadyTemplate() {
+        Long zoneId = 1L;
+        VMTemplateVO systemVmTemplate1 = mock(VMTemplateVO.class);
+        Mockito.when(systemVmTemplate1.getArch()).thenReturn(CPU.CPUArch.x86);
+        VMTemplateVO systemVmTemplate2 = mock(VMTemplateVO.class);
+        Mockito.when(systemVmTemplate2.getArch()).thenReturn(CPU.CPUArch.x86);
+        VMTemplateVO systemVmTemplate3 = mock(VMTemplateVO.class);
+        
Mockito.when(systemVmTemplate3.getArch()).thenReturn(CPU.CPUArch.arm64);
+        
Mockito.when(systemVmTemplate3.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
+        List<VMTemplateVO> templates = Arrays.asList(systemVmTemplate1, 
systemVmTemplate2, systemVmTemplate3);
+        
Mockito.when(hostDao.listDistinctHypervisorTypes(zoneId)).thenReturn(Arrays.asList(Hypervisor.HypervisorType.KVM));
+        SearchBuilder<VMTemplateVO> sb = mock(SearchBuilder.class);
+        templateDao.readySystemTemplateSearch = sb;
+        when(sb.create()).thenReturn(mock(SearchCriteria.class));
+        
doReturn(templates).when(templateDao).listBy(any(SearchCriteria.class), 
any(Filter.class));
+        VMTemplateVO readyTemplate = 
templateDao.findSystemVMReadyTemplate(zoneId, Hypervisor.HypervisorType.KVM, 
CPU.CPUArch.arm64.getType());
+        Assert.assertEquals(CPU.CPUArch.arm64, readyTemplate.getArch());
+    }
 }
diff --git 
a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java
 
b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java
index 411e6af883e..d1babb547f8 100644
--- 
a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java
+++ 
b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java
@@ -433,8 +433,14 @@ public class KubernetesClusterManagerImpl extends 
ManagerBase implements Kuberne
         return null;
     }
 
-    public VMTemplateVO getKubernetesServiceTemplate(DataCenter dataCenter, 
Hypervisor.HypervisorType hypervisorType) {
-        VMTemplateVO template = 
templateDao.findSystemVMReadyTemplate(dataCenter.getId(), hypervisorType);
+    public VMTemplateVO getKubernetesServiceTemplate(DataCenter dataCenter, 
Hypervisor.HypervisorType hypervisorType,
+                                                     
KubernetesSupportedVersion clusterKubernetesVersion) {
+        String systemVMPreferredArchitecture = 
ResourceManager.SystemVmPreferredArchitecture.valueIn(dataCenter.getId());
+        VMTemplateVO cksIso = clusterKubernetesVersion != null ?
+                templateDao.findById(clusterKubernetesVersion.getIsoId()) :
+                null;
+        String preferredArchitecture = 
getCksClusterPreferredArch(systemVMPreferredArchitecture, cksIso);
+        VMTemplateVO template = 
templateDao.findSystemVMReadyTemplate(dataCenter.getId(), hypervisorType, 
preferredArchitecture);
         if (DataCenter.Type.Edge.equals(dataCenter.getType()) && template != 
null && !template.isDirectDownload()) {
             logger.debug(String.format("Template %s can not be used for edge 
zone %s", template, dataCenter));
             template = templateDao.findRoutingTemplate(hypervisorType, 
networkHelper.getHypervisorRouterTemplateConfigMap().get(hypervisorType).valueIn(dataCenter.getId()));
@@ -445,6 +451,14 @@ public class KubernetesClusterManagerImpl extends 
ManagerBase implements Kuberne
         return  template;
     }
 
+    protected String getCksClusterPreferredArch(String 
systemVMPreferredArchitecture, VMTemplateVO cksIso) {
+        if (cksIso == null) {
+            return systemVMPreferredArchitecture;
+        }
+        String cksIsoArchName = cksIso.getArch().name();
+        return cksIsoArchName.equals(systemVMPreferredArchitecture) ? 
systemVMPreferredArchitecture : cksIsoArchName;
+    }
+
     protected void validateIsolatedNetworkIpRules(long ipId, 
FirewallRule.Purpose purpose, Network network, int clusterTotalNodeCount) {
         List<FirewallRuleVO> rules = 
firewallRulesDao.listByIpAndPurposeAndNotRevoked(ipId, purpose);
         for (FirewallRuleVO rule : rules) {
@@ -1302,7 +1316,10 @@ public class KubernetesClusterManagerImpl extends 
ManagerBase implements Kuberne
         }
 
         final Network defaultNetwork = 
getKubernetesClusterNetworkIfMissing(cmd.getName(), zone, owner, 
(int)controlNodeCount, (int)clusterSize, 
cmd.getExternalLoadBalancerIpAddress(), cmd.getNetworkId());
-        final VMTemplateVO finalTemplate = getKubernetesServiceTemplate(zone, 
deployDestination.getCluster().getHypervisorType());
+        final VMTemplateVO finalTemplate = getKubernetesServiceTemplate(zone, 
deployDestination.getCluster().getHypervisorType(), clusterKubernetesVersion);
+
+        
compareKubernetesIsoArchToSelectedTemplateArch(clusterKubernetesVersion, 
finalTemplate);
+
         final long cores = serviceOffering.getCpu() * (controlNodeCount + 
clusterSize);
         final long memory = serviceOffering.getRamSize() * (controlNodeCount + 
clusterSize);
 
@@ -1331,6 +1348,21 @@ public class KubernetesClusterManagerImpl extends 
ManagerBase implements Kuberne
         return cluster;
     }
 
+    private void 
compareKubernetesIsoArchToSelectedTemplateArch(KubernetesSupportedVersion 
clusterKubernetesVersion, VMTemplateVO finalTemplate) {
+        VMTemplateVO cksIso = 
templateDao.findById(clusterKubernetesVersion.getIsoId());
+        if (cksIso == null) {
+            String err = String.format("Cannot find Kubernetes ISO associated 
to the Kubernetes version %s (id=%s)",
+                    clusterKubernetesVersion.getName(), 
clusterKubernetesVersion.getUuid());
+            throw new CloudRuntimeException(err);
+        }
+        if (!cksIso.getArch().equals(finalTemplate.getArch())) {
+            String err = String.format("The selected Kubernetes ISO %s arch 
(%s) doesn't match the template %s arch (%s) " +
+                            "to deploy the Kubernetes cluster",
+                    clusterKubernetesVersion.getName(), cksIso.getArch(), 
finalTemplate.getName(), finalTemplate.getArch());
+            throw new CloudRuntimeException(err);
+        }
+    }
+
     private SecurityGroup getOrCreateSecurityGroupForAccount(Account owner) {
         String securityGroupName = String.format("%s-%s", 
KubernetesClusterActionWorker.CKS_CLUSTER_SECURITY_GROUP_NAME, owner.getUuid());
         String securityGroupDesc = String.format("%s and account %s", 
KubernetesClusterActionWorker.CKS_SECURITY_GROUP_DESCRIPTION, owner.getName());
diff --git 
a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java
 
b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java
index 29ecde477a4..99c4d4c9c96 100644
--- 
a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java
+++ 
b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java
@@ -29,6 +29,7 @@ import java.util.stream.Collectors;
 
 import javax.inject.Inject;
 
+import com.cloud.kubernetes.version.KubernetesSupportedVersionVO;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.LogManager;
@@ -194,7 +195,8 @@ public class KubernetesClusterActionWorker {
         DataCenterVO dataCenterVO = dataCenterDao.findById(zoneId);
         VMTemplateVO template = templateDao.findById(templateId);
         Hypervisor.HypervisorType type = template.getHypervisorType();
-        this.clusterTemplate = 
manager.getKubernetesServiceTemplate(dataCenterVO, type);
+        KubernetesSupportedVersionVO kubernetesSupportedVersion = 
kubernetesSupportedVersionDao.findById(this.kubernetesCluster.getKubernetesVersionId());
+        this.clusterTemplate = 
manager.getKubernetesServiceTemplate(dataCenterVO, type, 
kubernetesSupportedVersion);
         this.sshKeyFile = getManagementServerSshPublicKeyFile();
     }
 
diff --git 
a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java
 
b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java
index 5dfab87dd71..d92d0692ca1 100644
--- 
a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java
+++ 
b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java
@@ -250,7 +250,7 @@ public class KubernetesClusterResourceModifierActionWorker 
extends KubernetesClu
             for (Map.Entry<String, Pair<HostVO, Integer>> hostEntry : 
hosts_with_resevered_capacity.entrySet()) {
                 Pair<HostVO, Integer> hp = hostEntry.getValue();
                 HostVO h = hp.first();
-                if 
(!h.getHypervisorType().equals(clusterTemplate.getHypervisorType())) {
+                if 
(!h.getHypervisorType().equals(clusterTemplate.getHypervisorType()) || 
!h.getArch().equals(clusterTemplate.getArch())) {
                     continue;
                 }
                 hostDao.loadHostTags(h);
diff --git 
a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImplTest.java
 
b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImplTest.java
index a6d46ffc9aa..287e045a18e 100644
--- 
a/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImplTest.java
+++ 
b/plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImplTest.java
@@ -21,6 +21,7 @@ package com.cloud.kubernetes.cluster;
 
 import com.cloud.api.query.dao.TemplateJoinDao;
 import com.cloud.api.query.vo.TemplateJoinVO;
+import com.cloud.cpu.CPU;
 import com.cloud.dc.DataCenter;
 import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.exception.PermissionDeniedException;
@@ -292,4 +293,22 @@ public class KubernetesClusterManagerImplTest {
         
Mockito.when(kubernetesClusterDao.findById(Mockito.anyLong())).thenReturn(cluster);
         
Assert.assertTrue(kubernetesClusterManager.removeVmsFromCluster(cmd).size() > 
0);
     }
+
+    @Test
+    public void testGetCksClusterPreferredArchDifferentArchsPreferCKSIsoArch() 
{
+        String systemVMArch = "x86_64";
+        VMTemplateVO cksIso = Mockito.mock(VMTemplateVO.class);
+        Mockito.when(cksIso.getArch()).thenReturn(CPU.CPUArch.arm64);
+        String cksClusterPreferredArch = 
kubernetesClusterManager.getCksClusterPreferredArch(systemVMArch, cksIso);
+        Assert.assertEquals(CPU.CPUArch.arm64.name(), cksClusterPreferredArch);
+    }
+
+    @Test
+    public void testGetCksClusterPreferredArchSameArch() {
+        String systemVMArch = "x86_64";
+        VMTemplateVO cksIso = Mockito.mock(VMTemplateVO.class);
+        Mockito.when(cksIso.getArch()).thenReturn(CPU.CPUArch.amd64);
+        String cksClusterPreferredArch = 
kubernetesClusterManager.getCksClusterPreferredArch(systemVMArch, cksIso);
+        Assert.assertEquals(CPU.CPUArch.amd64.name(), cksClusterPreferredArch);
+    }
 }
diff --git 
a/plugins/storage/sharedfs/storagevm/src/main/java/org/apache/cloudstack/storage/sharedfs/lifecycle/StorageVmSharedFSLifeCycle.java
 
b/plugins/storage/sharedfs/storagevm/src/main/java/org/apache/cloudstack/storage/sharedfs/lifecycle/StorageVmSharedFSLifeCycle.java
index ed26963cf81..1850c3c218b 100644
--- 
a/plugins/storage/sharedfs/storagevm/src/main/java/org/apache/cloudstack/storage/sharedfs/lifecycle/StorageVmSharedFSLifeCycle.java
+++ 
b/plugins/storage/sharedfs/storagevm/src/main/java/org/apache/cloudstack/storage/sharedfs/lifecycle/StorageVmSharedFSLifeCycle.java
@@ -179,10 +179,11 @@ public class StorageVmSharedFSLifeCycle implements 
SharedFSLifeCycle {
             customParameterMap.put("maxIopsDo", maxIops.toString());
         }
         List<String> keypairs = new ArrayList<String>();
+        String preferredArchitecture = 
ResourceManager.SystemVmPreferredArchitecture.valueIn(zoneId);
 
         for (final Iterator<Hypervisor.HypervisorType> iter = 
hypervisors.iterator(); iter.hasNext();) {
             final Hypervisor.HypervisorType hypervisor = iter.next();
-            VMTemplateVO template = 
templateDao.findSystemVMReadyTemplate(zoneId, hypervisor);
+            VMTemplateVO template = 
templateDao.findSystemVMReadyTemplate(zoneId, hypervisor, 
preferredArchitecture);
             if (template == null && !iter.hasNext()) {
                 throw new CloudRuntimeException(String.format("Unable to find 
the systemvm template for %s or it was not downloaded in %s.", 
hypervisor.toString(), zone.toString()));
             }
diff --git 
a/plugins/storage/sharedfs/storagevm/src/test/java/org/apache/cloudstack/storage/sharedfs/lifecycle/StorageVmSharedFSLifeCycleTest.java
 
b/plugins/storage/sharedfs/storagevm/src/test/java/org/apache/cloudstack/storage/sharedfs/lifecycle/StorageVmSharedFSLifeCycleTest.java
index 2cc909ce0d7..bcd9d509a9a 100644
--- 
a/plugins/storage/sharedfs/storagevm/src/test/java/org/apache/cloudstack/storage/sharedfs/lifecycle/StorageVmSharedFSLifeCycleTest.java
+++ 
b/plugins/storage/sharedfs/storagevm/src/test/java/org/apache/cloudstack/storage/sharedfs/lifecycle/StorageVmSharedFSLifeCycleTest.java
@@ -236,7 +236,7 @@ public class StorageVmSharedFSLifeCycleTest {
         
when(serviceOfferingDao.findById(s_serviceOfferingId)).thenReturn(serviceOffering);
 
         VMTemplateVO template = mock(VMTemplateVO.class);
-        when(templateDao.findSystemVMReadyTemplate(s_zoneId, 
Hypervisor.HypervisorType.KVM)).thenReturn(template);
+        when(templateDao.findSystemVMReadyTemplate(s_zoneId, 
Hypervisor.HypervisorType.KVM, 
ResourceManager.SystemVmPreferredArchitecture.defaultValue())).thenReturn(template);
         when(template.getId()).thenReturn(s_templateId);
 
         return sharedFS;
@@ -303,7 +303,6 @@ public class StorageVmSharedFSLifeCycleTest {
         when(dataCenterDao.findById(s_zoneId)).thenReturn(zone);
         when(resourceMgr.getSupportedHypervisorTypes(s_zoneId, false, 
null)).thenReturn(List.of(Hypervisor.HypervisorType.KVM));
 
-        when(templateDao.findSystemVMReadyTemplate(s_zoneId, 
Hypervisor.HypervisorType.KVM)).thenReturn(null);
         lifeCycle.deploySharedFS(sharedFS, s_networkId, s_diskOfferingId, 
s_size, s_minIops, s_maxIops);
     }
 

Reply via email to