Skip to content

Veeam VM restore fails when the original template no longer exists #14291

Description

@akoskuczi-bw

problem

When restoring a KVM virtual machine using the CloudStack Veeam integration, the restore fails if the original template from which the VM was deployed has already been deleted from CloudStack.

The VM backup contains the complete root disk and the VM configuration, so the original template image should not be required to restore the VM.

However, the exported/restored VM metadata still references the original CloudStack template ID.
Example from the restore metadata:

<TemplateId>9c12bd7a-3a78-42dc-9bc2-20939014640f</TemplateId>
<TemplateName>9c12bd7a-3a78-42dc-9bc2-20939014640f</TemplateName>

<OriginalTemplateId>9c12bd7a-3a78-42dc-9bc2-20939014640f</OriginalTemplateId>
<OriginalTemplateName>9c12bd7a-3a78-42dc-9bc2-20939014640f</OriginalTemplateName>

The root disk section also contains the same template reference:
<rasd:Template>9c12bd7a-3a78-42dc-9bc2-20939014640f</rasd:Template>

If this template no longer exists in CloudStack, the VM cannot be restored.

NPE trace:
2026-10-01 14:19:00,743 ERROR [c.c.v.UserVmManagerImpl] (qtp1646029317-189:[ctx-780ca248, ctx-a2091f5e]) (logid:) error during resource reservation and allocation java.lang.NullPointerException: Cannot invoke "java.lang.Long.longValue()" because the return value of "com.cloud.storage.VMTemplateVO.getSize()" is null at com.cloud.vm.UserVmManagerImpl.validateRootDiskResize(UserVmManagerImpl.java:5363) at com.cloud.vm.UserVmManagerImpl.commitUserVm(UserVmManagerImpl.java:5102) at com.cloud.vm.UserVmManagerImpl.commitUserVm(UserVmManagerImpl.java:5340) at com.cloud.vm.UserVmManagerImpl.getUncheckedUserVmResource(UserVmManagerImpl.java:4829) at com.cloud.vm.UserVmManagerImpl.getCheckedUserVmResource(UserVmManagerImpl.java:4489) at com.cloud.vm.UserVmManagerImpl.createVirtualMachine(UserVmManagerImpl.java:4460) at com.cloud.vm.UserVmManagerImpl.createAdvancedVirtualMachine(UserVmManagerImpl.java:4232) at com.cloud.vm.UserVmManagerImpl.createVirtualMachine(UserVmManagerImpl.java:6842) at com.cloud.vm.UserVmManagerImpl.createVirtualMachine(UserVmManagerImpl.java:6763) 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.$Proxy159.createVirtualMachine(Unknown Source) at org.apache.cloudstack.veeam.adapter.ServerAdapter.lambda$createVmWithForRestoreIfNeeded$1(ServerAdapter.java:733) at com.cloud.utils.db.Transaction.execute(Transaction.java:38) at org.apache.cloudstack.veeam.adapter.ServerAdapter.createVmWithForRestoreIfNeeded(ServerAdapter.java:725) at org.apache.cloudstack.veeam.adapter.ServerAdapter.createInstance(ServerAdapter.java:825) at org.apache.cloudstack.veeam.adapter.ServerAdapter.createInstance(ServerAdapter.java:1375) at org.apache.cloudstack.veeam.adapter.ServerAdapter$$FastClassBySpringCGLIB$$89e9387c.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) at org.apache.cloudstack.veeam.adapter.ApiAccessInterceptor.invoke(ApiAccessInterceptor.java:63) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:175) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) 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.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) at org.apache.cloudstack.veeam.adapter.ServerAdapter$$EnhancerBySpringCGLIB$$153c7ba4.createInstance(<generated>) at org.apache.cloudstack.veeam.api.VmsRouteHandler.handlePost(VmsRouteHandler.java:263) at org.apache.cloudstack.veeam.api.VmsRouteHandler.handle(VmsRouteHandler.java:82) at org.apache.cloudstack.veeam.VeeamControlServlet.service(VeeamControlServlet.java:79) at javax.servlet.http.HttpServlet.service(HttpServlet.java:750) at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:799) at org.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1656) at org.apache.cloudstack.veeam.filter.BearerOrBasicAuthFilter.doFilter(BearerOrBasicAuthFilter.java:79) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1626) at org.apache.cloudstack.veeam.filter.AllowedClientCidrsFilter.doFilter(AllowedClientCidrsFilter.java:74) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1626) at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:552) at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233) at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1440) at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188) at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:505) at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186) at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1355) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:59) at org.eclipse.jetty.server.handler.RequestLogHandler.handle(RequestLogHandler.java:54) at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) at org.eclipse.jetty.server.Server.handle(Server.java:516) at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:487) at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:732) at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:479) at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:277) at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311) at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105) at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.onFillable(SslConnection.java:555) at org.eclipse.jetty.io.ssl.SslConnection.onFillable(SslConnection.java:410) at org.eclipse.jetty.io.ssl.SslConnection$2.succeeded(SslConnection.java:164) at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105) at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104) at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:338) at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:315) at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:173) at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131) at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:409) at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:883) at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1034) at java.base/java.lang.Thread.run(Thread.java:840)

versions

Ubuntu 24.04 KVM Hypervisors
Ubuntu 24.04 Management server
Cloudstack: 4.23.0.0

The steps to reproduce the bug

  1. Create a template in CloudStack.
  2. Deploy a VM from that template.
  3. Back up the VM using the CloudStack/Veeam integration.
  4. Verify that the VM backup is successful.
  5. Delete the original template from CloudStack.
  6. Attempt to restore the previously backed-up VM.
  7. The restore fails because the original template referenced by the VM metadata is no longer available.

What to do about it?

The restore process requires the original template referenced by TemplateId / OriginalTemplateId to still exist.
If the template has been deleted, the VM restore fails even though the backup contains the VM root disk.
A VM backup should be independently restorable even if the original template has been deleted.
During restore, the template reference should preferably be treated as provenance/metadata rather than as a mandatory dependency.

Possible behavior could be:
if original template exists:
restore VM and preserve the template relationship

if original template does not exist:
restore VM from the backed-up root disk
mark original template as unavailable / null / deleted

The existence of the original template should not prevent restoration of a VM whose root disk is fully contained in the backup. Deleting the original template does not normally make an already deployed VM unusable.
Therefore, a VM-level backup should ideally not introduce a stronger dependency on the original template than the running CloudStack VM itself has.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions