From a42798ab98cf68f3ad3904a1e02a8469509f582f Mon Sep 17 00:00:00 2001 From: Dogface2k <100990646+Dogface2k@users.noreply.github.com> Date: Sat, 8 Aug 2026 23:08:35 +0100 Subject: [PATCH] sharedfs: skip hypervisors without system templates --- .../lifecycle/StorageVmSharedFSLifeCycle.java | 5 ++- .../StorageVmSharedFSLifeCycleTest.java | 41 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) 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 ac8d6a58f0cb..15c45226e00a 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 @@ -180,7 +180,10 @@ private UserVm deploySharedFSVM(Long zoneId, Account owner, List networkId for (final Iterator iter = hypervisors.iterator(); iter.hasNext();) { final Hypervisor.HypervisorType hypervisor = iter.next(); VMTemplateVO template = templateDao.findSystemVMReadyTemplate(zoneId, hypervisor, preferredArchitecture); - if (template == null && !iter.hasNext()) { + if (template == null) { + if (iter.hasNext()) { + continue; + } 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 c64e8c05c995..dd7f873998ea 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 @@ -53,6 +53,7 @@ import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.UserVmDao; import java.io.IOException; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import org.apache.cloudstack.api.ApiCommandResourceType; @@ -273,6 +274,46 @@ public void testDeploySharedFS() throws ResourceUnavailableException, Insufficie Assert.assertEquals(Optional.ofNullable(result.second()), Optional.ofNullable(s_vmId)); } + @Test + public void testDeploySharedFSContinuesWhenTemplateIsMissingForNonLastHypervisor() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, IOException, OperationTimedoutException { + SharedFS sharedFS = prepareDeploySharedFS(); + when(resourceMgr.getSupportedHypervisorTypes(s_zoneId, false, null)).thenReturn(new ArrayList<>(List.of(Hypervisor.HypervisorType.External, Hypervisor.HypervisorType.KVM)) { + @Override + public Hypervisor.HypervisorType set(int index, Hypervisor.HypervisorType element) { + // Keep the test order stable while exercising the production shuffle call. + return get(index); + } + }); + when(templateDao.findSystemVMReadyTemplate(s_zoneId, Hypervisor.HypervisorType.External, ResourceManager.SystemVmPreferredArchitecture.defaultValue())).thenReturn(null); + + Account owner = mock(Account.class); + when(owner.getId()).thenReturn(s_ownerId); + when(accountMgr.getActiveAccountById(s_ownerId)).thenReturn(owner); + + UserVm vm = mock(UserVm.class); + when(vm.getId()).thenReturn(s_vmId); + when(userVmService.createAdvancedVirtualMachine( + any(DataCenter.class), any(ServiceOffering.class), any(VirtualMachineTemplate.class), anyList(), any(Account.class), anyString(), + anyString(), anyLong(), anyLong(), any(), isNull(), any(Hypervisor.HypervisorType.class), any(BaseCmd.HTTPMethod.class), anyString(), + isNull(), isNull(), anyList(), isNull(), any(Network.IpAddresses.class), isNull(), isNull(), isNull(), + anyMap(), isNull(), isNull(), isNull(), isNull(), + anyBoolean(), anyString(), isNull(), isNull(), isNull())).thenReturn(vm); + + VolumeVO rootVol = mock(VolumeVO.class); + when(rootVol.getVolumeType()).thenReturn(Volume.Type.ROOT); + when(rootVol.getName()).thenReturn("ROOT-1"); + VolumeVO dataVol = mock(VolumeVO.class); + when(dataVol.getId()).thenReturn(s_volumeId); + when(dataVol.getName()).thenReturn("DATA-1"); + when(dataVol.getVolumeType()).thenReturn(Volume.Type.DATADISK); + when(volumeDao.findByInstance(s_vmId)).thenReturn(List.of(rootVol, dataVol)); + + Pair result = lifeCycle.deploySharedFS(sharedFS, s_networkId, s_diskOfferingId, s_size, s_minIops, s_maxIops); + + Assert.assertEquals(Optional.ofNullable(result.first()), Optional.ofNullable(s_volumeId)); + Assert.assertEquals(Optional.ofNullable(result.second()), Optional.ofNullable(s_vmId)); + } + @Test(expected = CloudRuntimeException.class) public void testDeploySharedFSHypervisorNotFound() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, IOException, OperationTimedoutException { SharedFS sharedFS = mock(SharedFS.class);