This is an automated email from the ASF dual-hosted git repository.
RosiKyu pushed a commit to branch 4.22
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/4.22 by this push:
new 9030443eac5 SharedFS: fix NPE when accessing systemvm templates for
unsupported hypervisors (#13930)
9030443eac5 is described below
commit 9030443eac555c26b06bbf8e82249f2aac385fcc
Author: Abhisar Sinha <[email protected]>
AuthorDate: Fri Sep 11 20:14:32 2026 +0530
SharedFS: fix NPE when accessing systemvm templates for unsupported
hypervisors (#13930)
* fix NPE when accessing systemvm templates for unsupported hypervisors in
deploySharedFSVM()
* systemvm -> SystemVM
Co-authored-by: Copilot Autofix powered by AI
<[email protected]>
* Add unit test for skip-hypervisor-without-template fix in
deploySharedFSVM()
* sharedfs: drop duplicate skip-hypervisor test after 4.22 merge
---------
Co-authored-by: Copilot Autofix powered by AI
<[email protected]>
---
.../java/com/cloud/resource/ResourceService.java | 2 +-
.../lifecycle/StorageVmSharedFSLifeCycle.java | 5 +-
.../lifecycle/StorageVmSharedFSLifeCycleTest.java | 101 ++++++++++++---------
.../com/cloud/resource/ResourceManagerImpl.java | 4 +-
.../cloud/resource/MockResourceManagerImpl.java | 2 +-
5 files changed, 66 insertions(+), 48 deletions(-)
diff --git a/api/src/main/java/com/cloud/resource/ResourceService.java
b/api/src/main/java/com/cloud/resource/ResourceService.java
index 3cdf8fc64e9..202d7fe63c4 100644
--- a/api/src/main/java/com/cloud/resource/ResourceService.java
+++ b/api/src/main/java/com/cloud/resource/ResourceService.java
@@ -91,7 +91,7 @@ public interface ResourceService {
DataCenter getZone(Long zoneId);
- List<HypervisorType> getSupportedHypervisorTypes(long zoneId, boolean
forVirtualRouter, Long podId);
+ List<HypervisorType> getSupportedHypervisorTypes(long zoneId, boolean
forSystemVm, Long podId);
boolean releaseHostReservation(Long hostId);
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 15c45226e00..36b060fd615 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
@@ -160,7 +160,7 @@ public class StorageVmSharedFSLifeCycle implements
SharedFSLifeCycle {
ServiceOffering serviceOffering =
serviceOfferingDao.findById(serviceOfferingId);
DataCenter zone = dataCenterDao.findById(zoneId);
- List<Hypervisor.HypervisorType> hypervisors =
resourceMgr.getSupportedHypervisorTypes(zoneId, false, null);
+ List<Hypervisor.HypervisorType> hypervisors =
resourceMgr.getSupportedHypervisorTypes(zoneId, true, null);
if (hypervisors.size() > 0) {
Collections.shuffle(hypervisors);
} else {
@@ -183,8 +183,9 @@ public class StorageVmSharedFSLifeCycle implements
SharedFSLifeCycle {
if (template == null) {
if (iter.hasNext()) {
continue;
+ } else {
+ throw new CloudRuntimeException(String.format("Unable to
find the SystemVM template for any of the available hypervisors in zone %s.",
zone.toString()));
}
- throw new CloudRuntimeException(String.format("Unable to find
the systemvm template for %s or it was not downloaded in %s.",
hypervisor.toString(), zone.toString()));
}
LaunchPermissionVO existingPermission =
launchPermissionDao.findByTemplateAndAccount(template.getId(), owner.getId());
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 dd7f873998e..31d109e7d66 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
@@ -81,9 +81,12 @@ import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
@@ -230,7 +233,7 @@ public class StorageVmSharedFSLifeCycleTest {
DataCenterVO zone = mock(DataCenterVO.class);
when(dataCenterDao.findById(s_zoneId)).thenReturn(zone);
- when(resourceMgr.getSupportedHypervisorTypes(s_zoneId, false,
null)).thenReturn(List.of(Hypervisor.HypervisorType.KVM));
+ when(resourceMgr.getSupportedHypervisorTypes(s_zoneId, true,
null)).thenReturn(List.of(Hypervisor.HypervisorType.KVM));
ServiceOfferingVO serviceOffering = mock(ServiceOfferingVO.class);
when(serviceOfferingDao.findById(s_serviceOfferingId)).thenReturn(serviceOffering);
@@ -274,46 +277,6 @@ public class StorageVmSharedFSLifeCycleTest {
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<Long, Long> 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);
@@ -341,11 +304,65 @@ public class StorageVmSharedFSLifeCycleTest {
when(accountMgr.getActiveAccountById(s_ownerId)).thenReturn(null);
DataCenterVO zone = mock(DataCenterVO.class);
when(dataCenterDao.findById(s_zoneId)).thenReturn(zone);
- when(resourceMgr.getSupportedHypervisorTypes(s_zoneId, false,
null)).thenReturn(List.of(Hypervisor.HypervisorType.KVM));
+ when(resourceMgr.getSupportedHypervisorTypes(s_zoneId, true,
null)).thenReturn(List.of(Hypervisor.HypervisorType.KVM));
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);
+
+ 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);
diff --git a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
index 26532354720..275658a27e9 100755
--- a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
+++ b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
@@ -2962,7 +2962,7 @@ public class ResourceManagerImpl extends ManagerBase
implements ResourceManager,
}
@Override
- public List<HypervisorType> getSupportedHypervisorTypes(final long zoneId,
final boolean forVirtualRouter, final Long podId) {
+ public List<HypervisorType> getSupportedHypervisorTypes(final long zoneId,
final boolean forSystemVm, final Long podId) {
final List<HypervisorType> hypervisorTypes = new ArrayList<>();
List<ClusterVO> clustersForZone;
@@ -2974,7 +2974,7 @@ public class ResourceManagerImpl extends ManagerBase
implements ResourceManager,
for (final ClusterVO cluster : clustersForZone) {
final HypervisorType hType = cluster.getHypervisorType();
- if (!forVirtualRouter || (hType != HypervisorType.BareMetal &&
hType != HypervisorType.External && hType != HypervisorType.Ovm)) {
+ if (!forSystemVm || (hType != HypervisorType.BareMetal && hType !=
HypervisorType.External && hType != HypervisorType.Ovm)) {
hypervisorTypes.add(hType);
}
}
diff --git
a/server/src/test/java/com/cloud/resource/MockResourceManagerImpl.java
b/server/src/test/java/com/cloud/resource/MockResourceManagerImpl.java
index 8b62861165f..01be6db73d8 100755
--- a/server/src/test/java/com/cloud/resource/MockResourceManagerImpl.java
+++ b/server/src/test/java/com/cloud/resource/MockResourceManagerImpl.java
@@ -205,7 +205,7 @@ public class MockResourceManagerImpl extends ManagerBase
implements ResourceMana
* @see
com.cloud.resource.ResourceService#getSupportedHypervisorTypes(long, boolean,
java.lang.Long)
*/
@Override
- public List<HypervisorType> getSupportedHypervisorTypes(final long zoneId,
final boolean forVirtualRouter, final Long podId) {
+ public List<HypervisorType> getSupportedHypervisorTypes(final long zoneId,
final boolean forSystemVm, final Long podId) {
// TODO Auto-generated method stub
return null;
}