This is an automated email from the ASF dual-hosted git repository. weizhouapache pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/cloudstack.git
commit 5812290da14c89ace139dc04d948796c823099c9 Merge: 27722a5dd1b a8c8c18e56a Author: Wei Zhou <[email protected]> AuthorDate: Thu Sep 17 14:51:01 2026 +0200 Merge remote-tracking branch 'apache/4.22' .editorconfig | 26 +++++ .../java/com/cloud/resource/ResourceService.java | 2 +- .../storage/motion/AncientDataMotionStrategy.java | 8 ++ .../motion/AncientDataMotionStrategyTest.java | 15 +++ .../cloudstack/backup/NASBackupProvider.java | 42 ++++---- .../LibvirtRestoreBackupCommandWrapper.java | 46 ++++++--- .../wrapper/LibvirtTakeBackupCommandWrapper.java | 35 +++---- .../hypervisor/kvm/storage/KVMPhysicalDisk.java | 4 +- .../kvm/storage/LibvirtStorageAdaptor.java | 10 ++ .../LibvirtRestoreBackupCommandWrapperTest.java | 113 ++++++++++++++++++++- .../kvm/storage/KVMPhysicalDiskTest.java | 6 +- .../lifecycle/StorageVmSharedFSLifeCycle.java | 5 +- .../lifecycle/StorageVmSharedFSLifeCycleTest.java | 101 ++++++++++-------- .../com/cloud/resource/ResourceManagerImpl.java | 4 +- .../main/java/com/cloud/vm/UserVmManagerImpl.java | 14 ++- .../cloud/resource/MockResourceManagerImpl.java | 2 +- 16 files changed, 328 insertions(+), 105 deletions(-) diff --cc plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java index 8e6d33c4e66,08c54100bc2..d08b4775c8b --- a/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java +++ b/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java @@@ -61,7 -62,8 +61,8 @@@ import org.apache.cloudstack.framework. import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; + import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; diff --cc plugins/storage/sharedfs/storagevm/src/test/java/org/apache/cloudstack/storage/sharedfs/lifecycle/StorageVmSharedFSLifeCycleTest.java index f5b97a7d758,31d109e7d66..d9c7a671327 --- 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 @@@ -346,6 -309,60 +309,60 @@@ public class StorageVmSharedFSLifeCycle lifeCycle.deploySharedFS(sharedFS, s_networkId, s_diskOfferingId, s_size, s_minIops, s_maxIops); } + @Test + public void testDeploySharedFSSkipsHypervisorWithoutTemplate() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, IOException, OperationTimedoutException { + // Verifies that when the systemvm template is missing for one of the supported hypervisors, + // deploySharedFSVM() skips it and tries the next one instead of failing with an NPE. + SharedFS sharedFS = mock(SharedFS.class); + when(sharedFS.getDataCenterId()).thenReturn(s_zoneId); + when(sharedFS.getName()).thenReturn(s_name); + when(sharedFS.getServiceOfferingId()).thenReturn(s_serviceOfferingId); + when(sharedFS.getFsType()).thenReturn(SharedFS.FileSystemType.valueOf(s_fsFormat)); + when(sharedFS.getAccountId()).thenReturn(s_ownerId); + + Account owner = mock(Account.class); + when(owner.getId()).thenReturn(s_ownerId); + when(accountMgr.getActiveAccountById(s_ownerId)).thenReturn(owner); + + DataCenterVO zone = mock(DataCenterVO.class); + when(dataCenterDao.findById(s_zoneId)).thenReturn(zone); + + List<Hypervisor.HypervisorType> hypervisors = new ArrayList<>(List.of(Hypervisor.HypervisorType.XenServer, Hypervisor.HypervisorType.KVM)); + when(resourceMgr.getSupportedHypervisorTypes(s_zoneId, true, null)).thenReturn(hypervisors); + + ServiceOfferingVO serviceOffering = mock(ServiceOfferingVO.class); + when(serviceOfferingDao.findById(s_serviceOfferingId)).thenReturn(serviceOffering); + + // The hypervisor list is shuffled before iteration, so instead of pinning which hypervisor is + // tried first, the first lookup (whichever hypervisor that is) returns no template and the + // second lookup returns a valid one, exercising the "skip and try the next hypervisor" fix. + VMTemplateVO template = mock(VMTemplateVO.class); + when(template.getId()).thenReturn(s_templateId); + when(templateDao.findSystemVMReadyTemplate(eq(s_zoneId), any(Hypervisor.HypervisorType.class), eq(ResourceManager.SystemVmPreferredArchitecture.defaultValue()))) + .thenReturn(null, template); + + 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); ++ anyBoolean(), anyString(), isNull(), isNull(), isNull(), isNull())).thenReturn(vm); + + 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(dataVol)); + + Pair<Long, Long> result = lifeCycle.deploySharedFS(sharedFS, s_networkId, s_diskOfferingId, s_size, s_minIops, s_maxIops); + Assert.assertEquals(Optional.of(s_volumeId), Optional.ofNullable(result.first())); + Assert.assertEquals(Optional.of(s_vmId), Optional.ofNullable(result.second())); + + verify(templateDao, times(2)).findSystemVMReadyTemplate(eq(s_zoneId), any(Hypervisor.HypervisorType.class), eq(ResourceManager.SystemVmPreferredArchitecture.defaultValue())); + } + @Test public void testDeleteSharedFS() throws ResourceUnavailableException { SharedFS sharedFS = mock(SharedFS.class);
