From c588e67d6c62a0f08bbcce9a48b314ab3558d4d2 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Tue, 7 Apr 2026 12:57:52 +0530 Subject: [PATCH] changes for adding syncqueueitem for backup to block other operations on vm Signed-off-by: Abhishek Kumar --- ...ring-engine-orchestration-core-context.xml | 1 + .../backup/KVMBackupExportServiceImpl.java | 88 ++++++++++++++++++- .../backup/VmWorkWaitForBackupFinalize.java | 35 ++++++++ 3 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 server/src/main/java/org/apache/cloudstack/backup/VmWorkWaitForBackupFinalize.java diff --git a/engine/orchestration/src/main/resources/META-INF/cloudstack/core/spring-engine-orchestration-core-context.xml b/engine/orchestration/src/main/resources/META-INF/cloudstack/core/spring-engine-orchestration-core-context.xml index 17c5002c718..49c668f50e8 100644 --- a/engine/orchestration/src/main/resources/META-INF/cloudstack/core/spring-engine-orchestration-core-context.xml +++ b/engine/orchestration/src/main/resources/META-INF/cloudstack/core/spring-engine-orchestration-core-context.xml @@ -88,6 +88,7 @@ + diff --git a/server/src/main/java/org/apache/cloudstack/backup/KVMBackupExportServiceImpl.java b/server/src/main/java/org/apache/cloudstack/backup/KVMBackupExportServiceImpl.java index c859e888ac0..7ea30035a52 100644 --- a/server/src/main/java/org/apache/cloudstack/backup/KVMBackupExportServiceImpl.java +++ b/server/src/main/java/org/apache/cloudstack/backup/KVMBackupExportServiceImpl.java @@ -17,6 +17,9 @@ package org.apache.cloudstack.backup; +import static org.apache.cloudstack.backup.BackupManager.BackupFrameworkEnabled; +import static org.apache.cloudstack.backup.BackupManager.BackupProviderPlugin; + import java.time.Instant; import java.util.ArrayList; import java.util.Collections; @@ -45,6 +48,10 @@ import org.apache.cloudstack.backup.dao.BackupDao; import org.apache.cloudstack.backup.dao.ImageTransferDao; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.jobs.AsyncJobExecutionContext; +import org.apache.cloudstack.framework.jobs.AsyncJobManager; +import org.apache.cloudstack.framework.jobs.impl.VmWorkJobVO; +import org.apache.cloudstack.jobs.JobInfo; import org.apache.cloudstack.managed.context.ManagedContextTimerTask; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; @@ -71,23 +78,31 @@ import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.dao.VolumeDetailsDao; +import com.cloud.user.Account; import com.cloud.user.AccountService; import com.cloud.user.User; import com.cloud.utils.NumbersUtil; +import com.cloud.utils.Pair; +import com.cloud.utils.ReflectionUse; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VMInstanceDetailVO; import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.VmDetailConstants; +import com.cloud.vm.VmWork; +import com.cloud.vm.VmWorkConstants; +import com.cloud.vm.VmWorkJobHandler; +import com.cloud.vm.VmWorkJobHandlerProxy; +import com.cloud.vm.VmWorkSerializer; import com.cloud.vm.dao.VMInstanceDao; import com.cloud.vm.dao.VMInstanceDetailsDao; -import static org.apache.cloudstack.backup.BackupManager.BackupFrameworkEnabled; -import static org.apache.cloudstack.backup.BackupManager.BackupProviderPlugin; - @Component -public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackupExportService { +public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackupExportService, VmWorkJobHandler { + public static final String VM_WORK_JOB_HANDLER = KVMBackupExportServiceImpl.class.getSimpleName(); + private static final long BACKUP_FINALIZE_WAIT_CHECK_INTERVAL = 15 * 1000L; @Inject private VMInstanceDao vmInstanceDao; @@ -122,8 +137,13 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup @Inject AccountService accountService; + @Inject + AsyncJobManager asyncJobManager; + private Timer imageTransferTimer; + VmWorkJobHandlerProxy jobHandlerProxy = new VmWorkJobHandlerProxy(this); + private boolean isKVMBackupExportServiceSupported(Long zoneId) { return !BackupFrameworkEnabled.value() || StringUtils.equals("dummy", BackupProviderPlugin.valueIn(zoneId)); } @@ -189,6 +209,28 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup backupDao.remove(backup.getId()); } + protected void queueBackupFinalizeWaitWorkJob(final VMInstanceVO vm, final BackupVO backup) { + final CallContext context = CallContext.current(); + final Account callingAccount = context.getCallingAccount(); + final long callingUserId = context.getCallingUserId(); + + VmWorkJobVO workJob = new VmWorkJobVO(context.getContextId()); + workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER); + workJob.setCmd(VmWorkWaitForBackupFinalize.class.getName()); + workJob.setAccountId(callingAccount.getId()); + workJob.setUserId(callingUserId); + workJob.setStep(VmWorkJobVO.Step.Starting); + workJob.setVmType(VirtualMachine.Type.User); + workJob.setVmInstanceId(vm.getId()); + workJob.setRelated(AsyncJobExecutionContext.getOriginJobId()); + + VmWorkWaitForBackupFinalize workInfo = new VmWorkWaitForBackupFinalize( + callingUserId, callingAccount.getId(), vm.getId(), VM_WORK_JOB_HANDLER, backup.getId()); + workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo)); + + asyncJobManager.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId()); + } + @Override public Backup startBackup(StartBackupCmd cmd) { BackupVO backup = backupDao.findById(cmd.getEntityId()); @@ -246,6 +288,7 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup // Update backup with checkpoint creation time backup.setCheckpointCreateTime(answer.getCheckpointCreateTime()); updateBackupState(backup, Backup.Status.ReadyForTransfer); + queueBackupFinalizeWaitWorkJob(vm, backup); return backup; } @@ -873,6 +916,43 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup return true; } + @ReflectionUse + public Pair orchestrateWaitForBackupFinalize(VmWorkWaitForBackupFinalize work) { + return waitForBackupTerminalState(work.getBackupId()); + } + + @Override + public Pair handleVmWorkJob(VmWork work) throws Exception { + return jobHandlerProxy.handleVmWorkJob(work); + } + + protected Pair waitForBackupTerminalState(final long backupId) { + while (true) { + final BackupVO backup = backupDao.findByIdIncludingRemoved(backupId); + if (backup == null) { + RuntimeException ex = new CloudRuntimeException(String.format("Backup %d not found while waiting for finalize", backupId)); + return new Pair<>(JobInfo.Status.FAILED, asyncJobManager.marshallResultObject(ex)); + } + + if (backup.getStatus() == Backup.Status.BackedUp) { + return new Pair<>(JobInfo.Status.SUCCEEDED, asyncJobManager.marshallResultObject(backup.getId())); + } + + if (backup.getStatus() == Backup.Status.Failed || backup.getStatus() == Backup.Status.Error) { + RuntimeException ex = new CloudRuntimeException(String.format("Backup %d reached terminal failure state: %s", backupId, backup.getStatus())); + return new Pair<>(JobInfo.Status.FAILED, asyncJobManager.marshallResultObject(ex)); + } + + try { + Thread.sleep(BACKUP_FINALIZE_WAIT_CHECK_INTERVAL); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + RuntimeException ex = new CloudRuntimeException(String.format("Interrupted while waiting for backup %d finalize", backupId), e); + return new Pair<>(JobInfo.Status.FAILED, asyncJobManager.marshallResultObject(ex)); + } + } + } + private void pollImageTransferProgress() { try { List transferringTransfers = imageTransferDao.listByPhaseAndDirection( diff --git a/server/src/main/java/org/apache/cloudstack/backup/VmWorkWaitForBackupFinalize.java b/server/src/main/java/org/apache/cloudstack/backup/VmWorkWaitForBackupFinalize.java new file mode 100644 index 00000000000..ac64b47aa3e --- /dev/null +++ b/server/src/main/java/org/apache/cloudstack/backup/VmWorkWaitForBackupFinalize.java @@ -0,0 +1,35 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.backup; + +import com.cloud.vm.VmWork; + +public class VmWorkWaitForBackupFinalize extends VmWork { + private static final long serialVersionUID = 2209426364298601717L; + + private final long backupId; + + public VmWorkWaitForBackupFinalize(long userId, long accountId, long vmId, String handlerName, long backupId) { + super(userId, accountId, vmId, handlerName); + this.backupId = backupId; + } + + public long getBackupId() { + return backupId; + } +}