DeviC3 opened a new issue, #13816:
URL: https://github.com/apache/cloudstack/issues/13816

   ### problem
   
   Creating an instance from a backup (`createVMFromBackup` with 
`startvm=true`) fails with a `NullPointerException` in the API response, even 
though the instance is created correctly, its volumes are fully restored from 
the backup and the VM boots and runs.
   
   The user-visible effect is a red error in the UI with no indication that the 
operation actually succeeded, and the generated password is not returned.
   
   ```
   2026-08-05 18:37:06,819 ERROR [c.c.a.ApiAsyncJobDispatcher] 
(API-Job-Executor-20:[ctx-f743d928, job-138]) (logid:5f51fca6) Unexpected 
exception while executing 
org.apache.cloudstack.api.command.admin.vm.CreateVMFromBackupCmdByAdmin 
java.lang.NullPointerException: Cannot invoke "java.util.Map.get(Object)" 
because the return value of "com.cloud.utils.Pair.second()" is null
        at 
com.cloud.vm.UserVmManagerImpl.startVirtualMachine(UserVmManagerImpl.java:5342)
        at 
com.cloud.vm.UserVmManagerImpl.restoreVMFromBackup(UserVmManagerImpl.java:9848)
        at 
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at 
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at 
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:569)
        at 
org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
        at 
org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
        at 
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
        at 
org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
        at 
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
        at 
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215)
        at jdk.proxy3/jdk.proxy3.$Proxy202.restoreVMFromBackup(Unknown Source)
        at 
org.apache.cloudstack.api.command.user.vm.CreateVMFromBackupCmd.execute(CreateVMFromBackupCmd.java:124)
        at com.cloud.api.ApiDispatcher.dispatch(ApiDispatcher.java:173)
        at 
com.cloud.api.ApiAsyncJobDispatcher.runJob(ApiAsyncJobDispatcher.java:110)
        at 
org.apache.cloudstack.framework.jobs.impl.AsyncJobManagerImpl$5.runInContext(AsyncJobManagerImpl.java:698)
        ...
   2026-08-05 18:37:06,820 DEBUG [o.a.c.f.j.i.AsyncJobManagerImpl] 
(API-Job-Executor-20:[ctx-f743d928, job-138]) (logid:5f51fca6) Complete async 
job-138, jobStatus: FAILED, resultCode: 530, result: 
{"uuidList":[],"errorcode":"530","errortext":"Cannot invoke 
\"java.util.Map.get(Object)\" because the return value of 
\"com.cloud.utils.Pair.second()\" is null"}
   ```
   
   ### Root cause
   
   The trigger is a source VM whose template has `enable_password = 1` 
("Password Enabled").
   
   `restoreVMFromBackup` starts the VM **twice**:
   
   1. First with `Param.ReturnAfterVolumePrepare = true` - 
[UserVmManagerImpl.java#L9808](https://github.com/apache/cloudstack/blob/4.22.1.0/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java#L9808).
 On this run `vm.isUpdateParameters()` is still `true`, so the params map is 
built 
([L5795](https://github.com/apache/cloudstack/blob/4.22.1.0/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java#L5794-L5795))
 and the flag is then cleared - `vm.setUpdateParameters(false)` at 
[L5843](https://github.com/apache/cloudstack/blob/4.22.1.0/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java#L5835-L5845).
   2. Then, when `cmd.getStartVm()` is true, it performs the real start - 
[UserVmManagerImpl.java#L9848](https://github.com/apache/cloudstack/blob/4.22.1.0/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java#L9848).
   
   On the second run `vm.isUpdateParameters()` is already `false`, so `params` 
stays `null` and the returned `Pair.second()` is `null`. The private 
`startVirtualMachine` overload then dereferences it unconditionally whenever 
the template has a password enabled - 
[UserVmManagerImpl.java#L5339-L5342](https://github.com/apache/cloudstack/blob/4.22.1.0/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java#L5339-L5343):
   
   ```java
   VMTemplateVO template = 
_templateDao.findByIdIncludingRemoved(vm.getTemplateId());
   if (template.isEnablePassword()) {
       // this value is not being sent to the backend; need only for api 
display purposes
       
vm.setPassword((String)vmParamPair.second().get(VirtualMachineProfile.Param.VmPassword));
   }
   ```
   
   The exception is thrown **outside** the `try` block at 
[L9806-L9829](https://github.com/apache/cloudstack/blob/4.22.1.0/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java#L9806-L9829),
 so the `expunge()` cleanup in the catch does not run. That is why the VM 
survives in a correct, running state and only the API response fails - the 
`VM.CREATE.FROM.BACKUP` event itself is recorded as `Completed`.
   
   The same unguarded dereference is present on `main` - 
[UserVmManagerImpl.java#L5597](https://github.com/apache/cloudstack/blob/main/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java#L5597).
   
   ### versions
   
   - CloudStack **4.22.1.0** (packages `cloudstack-management-4.22.1.0-1`, 
`cloudstack-agent-4.22.1.0-1`); code inspected on tag `4.22.1.0` and on `main`
   - Hypervisor: **KVM** on AlmaLinux 9.8, `qemu-kvm 10.1.0`, `libvirt 
11.10.0`, OpenJDK 17.0.20
   - Primary storage: **Ceph RBD** (Ceph 20.2.2)
   - Backup provider: **NAS Backup & Recovery** 
(`backup.framework.provider.plugin = nas`), repository on an NFS export backed 
by CephFS
   - Database: MySQL 8.0.46
   - 3 management servers, single advanced zone
   
   ### The steps to reproduce the bug
   
   1. Register a template with **Password Enabled = true** (`enable_password = 
1`).
   2. Deploy an instance from that template.
   3. Enable the backup framework with the `nas` provider, assign a backup 
offering to the instance and take a backup.
   4. Go to **Backups**, select the backup, choose **Create Instance from 
Backup** and leave **Start Instance = Yes**.
   5. The job fails with the NPE above (`errorcode 530`), but 
`listVirtualMachines` shows the new instance in state `Running` with its 
volumes correctly restored.
   
   The bug does not appear when the template has Password Enabled unset, or 
when **Start Instance = No** is selected.
   
   
   ### What to do about it?
   
   Workaround for users: create the instance with **Start Instance = No** and 
start it afterwards - the manual start path (`StartVMCmd`) only uses 
`Pair.first()`, so it is unaffected.
   
   Minimal fix - guard the dereference at `UserVmManagerImpl.java:5342`:
   
   ```java
   if (template.isEnablePassword() && vmParamPair.second() != null) {
       
vm.setPassword((String)vmParamPair.second().get(VirtualMachineProfile.Param.VmPassword));
   }
   ```
   
   A cleaner fix would be for `restoreVMFromBackup` to reuse the params map 
returned by the first `startVirtualMachine` call instead of letting the second 
call re-derive it from a flag the first call has already consumed - that would 
also make the generated password available in the API response, which is 
arguably the intended behaviour.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to