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);

Reply via email to