Skip to content

Commit ed0d606

Browse files
authored
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
1 parent 6589235 commit ed0d606

File tree

9 files changed

+93
-10
lines changed

9 files changed

+93
-10
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
@@ -72,7 +72,7 @@ public interface VMTemplateDao extends GenericDao<VMTemplateVO, Long>, StateDao<
7272

7373
VMTemplateVO findSystemVMTemplate(long zoneId);
7474

75-
VMTemplateVO findSystemVMReadyTemplate(long zoneId, HypervisorType hypervisorType);
75+
VMTemplateVO findSystemVMReadyTemplate(long zoneId, HypervisorType hypervisorType, String preferredArch);
7676

7777
List<VMTemplateVO> findSystemVMReadyTemplates(long zoneId, HypervisorType hypervisorType, String preferredArch);
7878

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
@@ -23,6 +23,7 @@
2323
import java.util.HashMap;
2424
import java.util.List;
2525
import java.util.Map;
26+
import java.util.stream.Collectors;
2627

2728
import javax.inject.Inject;
2829
import javax.naming.ConfigurationException;
@@ -578,11 +579,19 @@ public List<VMTemplateVO> listAllReadySystemVMTemplates(Long zoneId) {
578579
}
579580

580581
@Override
581-
public VMTemplateVO findSystemVMReadyTemplate(long zoneId, HypervisorType hypervisorType) {
582+
public VMTemplateVO findSystemVMReadyTemplate(long zoneId, HypervisorType hypervisorType, String preferredArch) {
582583
List<VMTemplateVO> templates = listAllReadySystemVMTemplates(zoneId);
583584
if (CollectionUtils.isEmpty(templates)) {
584585
return null;
585586
}
587+
if (StringUtils.isNotBlank(preferredArch)) {
588+
// Sort the templates by preferred architecture first
589+
templates = templates.stream()
590+
.sorted(Comparator.comparing(
591+
x -> !x.getArch().getType().equalsIgnoreCase(preferredArch)
592+
))
593+
.collect(Collectors.toList());
594+
}
586595
if (hypervisorType == HypervisorType.Any) {
587596
return templates.get(0);
588597
}

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import java.util.List;
3232
import java.util.Map;
3333

34+
import org.junit.Assert;
3435
import org.junit.Test;
3536
import org.junit.runner.RunWith;
3637
import org.mockito.InjectMocks;
@@ -186,4 +187,24 @@ public void testFindLatestTemplateByTypeAndHypervisorAndArch_NotFound() {
186187
VMTemplateVO result = templateDao.findLatestTemplateByTypeAndHypervisorAndArch(hypervisorType, arch, type);
187188
assertNull(result);
188189
}
190+
191+
@Test
192+
public void testFindSystemVMReadyTemplate() {
193+
Long zoneId = 1L;
194+
VMTemplateVO systemVmTemplate1 = mock(VMTemplateVO.class);
195+
Mockito.when(systemVmTemplate1.getArch()).thenReturn(CPU.CPUArch.x86);
196+
VMTemplateVO systemVmTemplate2 = mock(VMTemplateVO.class);
197+
Mockito.when(systemVmTemplate2.getArch()).thenReturn(CPU.CPUArch.x86);
198+
VMTemplateVO systemVmTemplate3 = mock(VMTemplateVO.class);
199+
Mockito.when(systemVmTemplate3.getArch()).thenReturn(CPU.CPUArch.arm64);
200+
Mockito.when(systemVmTemplate3.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM);
201+
List<VMTemplateVO> templates = Arrays.asList(systemVmTemplate1, systemVmTemplate2, systemVmTemplate3);
202+
Mockito.when(hostDao.listDistinctHypervisorTypes(zoneId)).thenReturn(Arrays.asList(Hypervisor.HypervisorType.KVM));
203+
SearchBuilder<VMTemplateVO> sb = mock(SearchBuilder.class);
204+
templateDao.readySystemTemplateSearch = sb;
205+
when(sb.create()).thenReturn(mock(SearchCriteria.class));
206+
doReturn(templates).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class));
207+
VMTemplateVO readyTemplate = templateDao.findSystemVMReadyTemplate(zoneId, Hypervisor.HypervisorType.KVM, CPU.CPUArch.arm64.getType());
208+
Assert.assertEquals(CPU.CPUArch.arm64, readyTemplate.getArch());
209+
}
189210
}

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

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -433,8 +433,14 @@ private IpAddress getSourceNatIp(Network network) {
433433
return null;
434434
}
435435

436-
public VMTemplateVO getKubernetesServiceTemplate(DataCenter dataCenter, Hypervisor.HypervisorType hypervisorType) {
437-
VMTemplateVO template = templateDao.findSystemVMReadyTemplate(dataCenter.getId(), hypervisorType);
436+
public VMTemplateVO getKubernetesServiceTemplate(DataCenter dataCenter, Hypervisor.HypervisorType hypervisorType,
437+
KubernetesSupportedVersion clusterKubernetesVersion) {
438+
String systemVMPreferredArchitecture = ResourceManager.SystemVmPreferredArchitecture.valueIn(dataCenter.getId());
439+
VMTemplateVO cksIso = clusterKubernetesVersion != null ?
440+
templateDao.findById(clusterKubernetesVersion.getIsoId()) :
441+
null;
442+
String preferredArchitecture = getCksClusterPreferredArch(systemVMPreferredArchitecture, cksIso);
443+
VMTemplateVO template = templateDao.findSystemVMReadyTemplate(dataCenter.getId(), hypervisorType, preferredArchitecture);
438444
if (DataCenter.Type.Edge.equals(dataCenter.getType()) && template != null && !template.isDirectDownload()) {
439445
logger.debug(String.format("Template %s can not be used for edge zone %s", template, dataCenter));
440446
template = templateDao.findRoutingTemplate(hypervisorType, networkHelper.getHypervisorRouterTemplateConfigMap().get(hypervisorType).valueIn(dataCenter.getId()));
@@ -445,6 +451,14 @@ public VMTemplateVO getKubernetesServiceTemplate(DataCenter dataCenter, Hypervis
445451
return template;
446452
}
447453

454+
protected String getCksClusterPreferredArch(String systemVMPreferredArchitecture, VMTemplateVO cksIso) {
455+
if (cksIso == null) {
456+
return systemVMPreferredArchitecture;
457+
}
458+
String cksIsoArchName = cksIso.getArch().name();
459+
return cksIsoArchName.equals(systemVMPreferredArchitecture) ? systemVMPreferredArchitecture : cksIsoArchName;
460+
}
461+
448462
protected void validateIsolatedNetworkIpRules(long ipId, FirewallRule.Purpose purpose, Network network, int clusterTotalNodeCount) {
449463
List<FirewallRuleVO> rules = firewallRulesDao.listByIpAndPurposeAndNotRevoked(ipId, purpose);
450464
for (FirewallRuleVO rule : rules) {
@@ -1302,7 +1316,10 @@ public KubernetesCluster createManagedKubernetesCluster(CreateKubernetesClusterC
13021316
}
13031317

13041318
final Network defaultNetwork = getKubernetesClusterNetworkIfMissing(cmd.getName(), zone, owner, (int)controlNodeCount, (int)clusterSize, cmd.getExternalLoadBalancerIpAddress(), cmd.getNetworkId());
1305-
final VMTemplateVO finalTemplate = getKubernetesServiceTemplate(zone, deployDestination.getCluster().getHypervisorType());
1319+
final VMTemplateVO finalTemplate = getKubernetesServiceTemplate(zone, deployDestination.getCluster().getHypervisorType(), clusterKubernetesVersion);
1320+
1321+
compareKubernetesIsoArchToSelectedTemplateArch(clusterKubernetesVersion, finalTemplate);
1322+
13061323
final long cores = serviceOffering.getCpu() * (controlNodeCount + clusterSize);
13071324
final long memory = serviceOffering.getRamSize() * (controlNodeCount + clusterSize);
13081325

@@ -1331,6 +1348,21 @@ public KubernetesClusterVO doInTransaction(TransactionStatus status) {
13311348
return cluster;
13321349
}
13331350

1351+
private void compareKubernetesIsoArchToSelectedTemplateArch(KubernetesSupportedVersion clusterKubernetesVersion, VMTemplateVO finalTemplate) {
1352+
VMTemplateVO cksIso = templateDao.findById(clusterKubernetesVersion.getIsoId());
1353+
if (cksIso == null) {
1354+
String err = String.format("Cannot find Kubernetes ISO associated to the Kubernetes version %s (id=%s)",
1355+
clusterKubernetesVersion.getName(), clusterKubernetesVersion.getUuid());
1356+
throw new CloudRuntimeException(err);
1357+
}
1358+
if (!cksIso.getArch().equals(finalTemplate.getArch())) {
1359+
String err = String.format("The selected Kubernetes ISO %s arch (%s) doesn't match the template %s arch (%s) " +
1360+
"to deploy the Kubernetes cluster",
1361+
clusterKubernetesVersion.getName(), cksIso.getArch(), finalTemplate.getName(), finalTemplate.getArch());
1362+
throw new CloudRuntimeException(err);
1363+
}
1364+
}
1365+
13341366
private SecurityGroup getOrCreateSecurityGroupForAccount(Account owner) {
13351367
String securityGroupName = String.format("%s-%s", KubernetesClusterActionWorker.CKS_CLUSTER_SECURITY_GROUP_NAME, owner.getUuid());
13361368
String securityGroupDesc = String.format("%s and account %s", KubernetesClusterActionWorker.CKS_SECURITY_GROUP_DESCRIPTION, owner.getName());

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
@@ -29,6 +29,7 @@
2929

3030
import javax.inject.Inject;
3131

32+
import com.cloud.kubernetes.version.KubernetesSupportedVersionVO;
3233
import org.apache.logging.log4j.Level;
3334
import org.apache.logging.log4j.Logger;
3435
import org.apache.logging.log4j.LogManager;
@@ -194,7 +195,8 @@ protected void init() {
194195
DataCenterVO dataCenterVO = dataCenterDao.findById(zoneId);
195196
VMTemplateVO template = templateDao.findById(templateId);
196197
Hypervisor.HypervisorType type = template.getHypervisorType();
197-
this.clusterTemplate = manager.getKubernetesServiceTemplate(dataCenterVO, type);
198+
KubernetesSupportedVersionVO kubernetesSupportedVersion = kubernetesSupportedVersionDao.findById(this.kubernetesCluster.getKubernetesVersionId());
199+
this.clusterTemplate = manager.getKubernetesServiceTemplate(dataCenterVO, type, kubernetesSupportedVersion);
198200
this.sshKeyFile = getManagementServerSshPublicKeyFile();
199201
}
200202

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
@@ -250,7 +250,7 @@ protected DeployDestination plan(final long nodesCount, final DataCenter zone, f
250250
for (Map.Entry<String, Pair<HostVO, Integer>> hostEntry : hosts_with_resevered_capacity.entrySet()) {
251251
Pair<HostVO, Integer> hp = hostEntry.getValue();
252252
HostVO h = hp.first();
253-
if (!h.getHypervisorType().equals(clusterTemplate.getHypervisorType())) {
253+
if (!h.getHypervisorType().equals(clusterTemplate.getHypervisorType()) || !h.getArch().equals(clusterTemplate.getArch())) {
254254
continue;
255255
}
256256
hostDao.loadHostTags(h);

plugins/integrations/kubernetes-service/src/test/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImplTest.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import com.cloud.api.query.dao.TemplateJoinDao;
2323
import com.cloud.api.query.vo.TemplateJoinVO;
24+
import com.cloud.cpu.CPU;
2425
import com.cloud.dc.DataCenter;
2526
import com.cloud.exception.InvalidParameterValueException;
2627
import com.cloud.exception.PermissionDeniedException;
@@ -292,4 +293,22 @@ public void removeVmsFromCluster() {
292293
Mockito.when(kubernetesClusterDao.findById(Mockito.anyLong())).thenReturn(cluster);
293294
Assert.assertTrue(kubernetesClusterManager.removeVmsFromCluster(cmd).size() > 0);
294295
}
296+
297+
@Test
298+
public void testGetCksClusterPreferredArchDifferentArchsPreferCKSIsoArch() {
299+
String systemVMArch = "x86_64";
300+
VMTemplateVO cksIso = Mockito.mock(VMTemplateVO.class);
301+
Mockito.when(cksIso.getArch()).thenReturn(CPU.CPUArch.arm64);
302+
String cksClusterPreferredArch = kubernetesClusterManager.getCksClusterPreferredArch(systemVMArch, cksIso);
303+
Assert.assertEquals(CPU.CPUArch.arm64.name(), cksClusterPreferredArch);
304+
}
305+
306+
@Test
307+
public void testGetCksClusterPreferredArchSameArch() {
308+
String systemVMArch = "x86_64";
309+
VMTemplateVO cksIso = Mockito.mock(VMTemplateVO.class);
310+
Mockito.when(cksIso.getArch()).thenReturn(CPU.CPUArch.amd64);
311+
String cksClusterPreferredArch = kubernetesClusterManager.getCksClusterPreferredArch(systemVMArch, cksIso);
312+
Assert.assertEquals(CPU.CPUArch.amd64.name(), cksClusterPreferredArch);
313+
}
295314
}

plugins/storage/sharedfs/storagevm/src/main/java/org/apache/cloudstack/storage/sharedfs/lifecycle/StorageVmSharedFSLifeCycle.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,10 +179,11 @@ private UserVm deploySharedFSVM(Long zoneId, Account owner, List<Long> networkId
179179
customParameterMap.put("maxIopsDo", maxIops.toString());
180180
}
181181
List<String> keypairs = new ArrayList<String>();
182+
String preferredArchitecture = ResourceManager.SystemVmPreferredArchitecture.valueIn(zoneId);
182183

183184
for (final Iterator<Hypervisor.HypervisorType> iter = hypervisors.iterator(); iter.hasNext();) {
184185
final Hypervisor.HypervisorType hypervisor = iter.next();
185-
VMTemplateVO template = templateDao.findSystemVMReadyTemplate(zoneId, hypervisor);
186+
VMTemplateVO template = templateDao.findSystemVMReadyTemplate(zoneId, hypervisor, preferredArchitecture);
186187
if (template == null && !iter.hasNext()) {
187188
throw new CloudRuntimeException(String.format("Unable to find the systemvm template for %s or it was not downloaded in %s.", hypervisor.toString(), zone.toString()));
188189
}

plugins/storage/sharedfs/storagevm/src/test/java/org/apache/cloudstack/storage/sharedfs/lifecycle/StorageVmSharedFSLifeCycleTest.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ private SharedFS prepareDeploySharedFS() throws ResourceUnavailableException, In
236236
when(serviceOfferingDao.findById(s_serviceOfferingId)).thenReturn(serviceOffering);
237237

238238
VMTemplateVO template = mock(VMTemplateVO.class);
239-
when(templateDao.findSystemVMReadyTemplate(s_zoneId, Hypervisor.HypervisorType.KVM)).thenReturn(template);
239+
when(templateDao.findSystemVMReadyTemplate(s_zoneId, Hypervisor.HypervisorType.KVM, ResourceManager.SystemVmPreferredArchitecture.defaultValue())).thenReturn(template);
240240
when(template.getId()).thenReturn(s_templateId);
241241

242242
return sharedFS;
@@ -303,7 +303,6 @@ public void testDeploySharedFSTemplateNotFound() throws ResourceUnavailableExcep
303303
when(dataCenterDao.findById(s_zoneId)).thenReturn(zone);
304304
when(resourceMgr.getSupportedHypervisorTypes(s_zoneId, false, null)).thenReturn(List.of(Hypervisor.HypervisorType.KVM));
305305

306-
when(templateDao.findSystemVMReadyTemplate(s_zoneId, Hypervisor.HypervisorType.KVM)).thenReturn(null);
307306
lifeCycle.deploySharedFS(sharedFS, s_networkId, s_diskOfferingId, s_size, s_minIops, s_maxIops);
308307
}
309308

0 commit comments

Comments
 (0)