Fix NPE on external/unmanaged instance import using custom offerings#12884
Fix NPE on external/unmanaged instance import using custom offerings#12884winterhazel wants to merge 1 commit intoapache:4.20from
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## 4.20 #12884 +/- ##
=========================================
Coverage 16.25% 16.25%
- Complexity 13415 13422 +7
=========================================
Files 5664 5664
Lines 500465 500507 +42
Branches 60780 60785 +5
=========================================
+ Hits 81338 81372 +34
- Misses 410031 410041 +10
+ Partials 9096 9094 -2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| sourceVMwareInstance = sourceInstanceDetails.first(); | ||
| isClonedInstance = sourceInstanceDetails.second(); | ||
|
|
||
| // Ensure that the configured resource limits will not be exceeded before beggining the conversion process |
There was a problem hiding this comment.
| // Ensure that the configured resource limits will not be exceeded before beggining the conversion process | |
| // Ensure that the configured resource limits will not be exceeded before beginning the conversion process |
| } | ||
|
|
||
| private void checkVmResourceLimitsForUnmanagedInstanceImport(Account owner, UnmanagedInstanceTO unmanagedInstance, ServiceOfferingVO serviceOffering, VMTemplateVO template, List<Reserver> reservations) throws ResourceAllocationException { | ||
| // When importing a unmanaged instance, the amount of CPUs and memory is obtained from the hypervisor unless powered off |
There was a problem hiding this comment.
| // When importing a unmanaged instance, the amount of CPUs and memory is obtained from the hypervisor unless powered off | |
| // When importing an unmanaged instance, the amount of CPUs and memory is obtained from the hypervisor unless powered off |
There was a problem hiding this comment.
Pull request overview
Fixes a regression where importing unmanaged/external instances with custom (dynamic) compute offerings can hit NPEs when CPU/RAM are not populated on the offering, by deriving CPU/RAM from the hypervisor (unmanaged) or from provided details (external/disk-based imports) and moving resource-limit reservations closer to the specific import paths.
Changes:
- Move VM (cpu/memory/vm count) resource-limit reservations out of top-level import methods and into specific import flows.
- Add pre-checks to determine CPU/RAM for unmanaged-instance imports (hypervisor vs offering) and for external KVM imports (offering vs
details). - Ensure reservations are closed via
ReservationHelper.closeAll(...)around import/conversion flows.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| sourceVMwareInstance = sourceInstanceDetails.first(); | ||
| isClonedInstance = sourceInstanceDetails.second(); | ||
|
|
||
| // Ensure that the configured resource limits will not be exceeded before beggining the conversion process |
There was a problem hiding this comment.
Typo in comment: “beggining” → “beginning”.
| // Ensure that the configured resource limits will not be exceeded before beggining the conversion process | |
| // Ensure that the configured resource limits will not be exceeded before beginning the conversion process |
| private void checkVmResourceLimitsForExternalKvmVmImport(Account owner, ServiceOfferingVO serviceOffering, VMTemplateVO template, Map<String, String> details, List<Reserver> reservations) throws ResourceAllocationException { | ||
| // When importing an external VM, the amount of CPUs and memory is always obtained from the compute offering, | ||
| // unlike the unmanaged instance import that obtains it from the hypervisor unless the VM is powered off and the offering is fixed | ||
| Integer cpu = serviceOffering.getCpu(); | ||
| Integer memory = serviceOffering.getRamSize(); | ||
|
|
||
| if (serviceOffering.isDynamic()) { | ||
| cpu = getDetailAsInteger(VmDetailConstants.CPU_NUMBER, details); | ||
| memory = getDetailAsInteger(VmDetailConstants.MEMORY, details); | ||
| } | ||
|
|
||
| List<String> resourceLimitHostTags = resourceLimitService.getResourceLimitHostTags(serviceOffering, template); | ||
|
|
||
| CheckedReservation vmReservation = new CheckedReservation(owner, Resource.ResourceType.user_vm, resourceLimitHostTags, 1L, reservationDao, resourceLimitService); | ||
| reservations.add(vmReservation); | ||
|
|
||
| CheckedReservation cpuReservation = new CheckedReservation(owner, Resource.ResourceType.cpu, resourceLimitHostTags, cpu.longValue(), reservationDao, resourceLimitService); | ||
| reservations.add(cpuReservation); | ||
|
|
||
| CheckedReservation memReservation = new CheckedReservation(owner, Resource.ResourceType.memory, resourceLimitHostTags, memory.longValue(), reservationDao, resourceLimitService); | ||
| reservations.add(memReservation); | ||
| } |
There was a problem hiding this comment.
New VM resource-limit selection logic (dynamic vs fixed offering, powered-off vs running unmanaged VM, and parsing CPU/memory from details) is not covered by unit tests in UnmanagedVMsManagerImplTest. Add focused tests for: (1) unmanaged import with null/unknown powerState, (2) external import with dynamic offering reading cpuNumber/memory, and (3) validation failures for missing/invalid values, to prevent regressions back to NPEs.
| Integer cpu = serviceOffering.getCpu(); | ||
| Integer memory = serviceOffering.getRamSize(); | ||
|
|
||
| if (serviceOffering.isDynamic() || !unmanagedInstance.getPowerState().equals(UnmanagedInstanceTO.PowerState.PowerOff)) { |
There was a problem hiding this comment.
unmanagedInstance.getPowerState() can be null (see UnmanagedInstanceTO), so calling .equals(PowerOff) here can throw an NPE during import. Make this null-safe (e.g., compare with PowerOff.equals(...) or treat null/unknown as not-PowerOff) so the resource-limit check can’t crash the import path.
| if (serviceOffering.isDynamic() || !unmanagedInstance.getPowerState().equals(UnmanagedInstanceTO.PowerState.PowerOff)) { | |
| if (serviceOffering.isDynamic() || !UnmanagedInstanceTO.PowerState.PowerOff.equals(unmanagedInstance.getPowerState())) { |
| Integer cpu = serviceOffering.getCpu(); | ||
| Integer memory = serviceOffering.getRamSize(); | ||
|
|
||
| if (serviceOffering.isDynamic()) { | ||
| cpu = getDetailAsInteger(VmDetailConstants.CPU_NUMBER, details); | ||
| memory = getDetailAsInteger(VmDetailConstants.MEMORY, details); | ||
| } | ||
|
|
||
| List<String> resourceLimitHostTags = resourceLimitService.getResourceLimitHostTags(serviceOffering, template); | ||
|
|
||
| CheckedReservation vmReservation = new CheckedReservation(owner, Resource.ResourceType.user_vm, resourceLimitHostTags, 1L, reservationDao, resourceLimitService); | ||
| reservations.add(vmReservation); | ||
|
|
||
| CheckedReservation cpuReservation = new CheckedReservation(owner, Resource.ResourceType.cpu, resourceLimitHostTags, cpu.longValue(), reservationDao, resourceLimitService); | ||
| reservations.add(cpuReservation); | ||
|
|
||
| CheckedReservation memReservation = new CheckedReservation(owner, Resource.ResourceType.memory, resourceLimitHostTags, memory.longValue(), reservationDao, resourceLimitService); | ||
| reservations.add(memReservation); |
There was a problem hiding this comment.
checkVmResourceLimitsForExternalKvmVmImport can still throw an NPE: if serviceOffering.getCpu() / getRamSize() are null (e.g., custom offering metadata) and the offering is not treated as dynamic here, cpu.longValue() / memory.longValue() will crash. Add explicit validation (null/<=0) for cpu and memory (both for dynamic and non-dynamic) and throw a parameter/internal error before constructing reservations.
|
@blueorangutan package |
|
@abh1sar a [SL] Jenkins job has been kicked to build packages. It will be bundled with KVM, XenServer and VMware SystemVM templates. I'll keep you posted as I make progress. |
|
Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 17237 |
Description
This PR addresses a regression in the import of unmanaged/external VMs using a custom compute offering (reported in https://lists.apache.org/thread/1bvxjc197zhj61mtjxpm3tz1o27znjmv).
As both
serviceOffering.getCpu()andserviceOffering.getRamSize()return null when the offering is custom constrained/unconstrained, we need to check the amount of CPUs and memory returned by the hypervisor in case an unmanaged instance is being imported, or thecpuNumberandmemorydetails in case the instance belongs to a remote host/is being imported from its disk.Types of changes
Feature/Enhancement Scale or Bug Severity
Bug Severity
Screenshots (if appropriate):
How Has This Been Tested?
Tests are still pending. The following operations need to be validated with both fixed and custom offerings: