From e32a6ab7d945a9085c7131574ffabe27b37d769b Mon Sep 17 00:00:00 2001 From: Abhisar Sinha <63767682+abh1sar@users.noreply.github.com> Date: Tue, 31 Mar 2026 07:52:56 +0530 Subject: [PATCH] Make veeam-kvm exclusive with other providers finalize all pending image transfers when finalize backup is called --- .../cloudstack/backup/BackupManager.java | 4 +-- .../backup/KVMBackupExportService.java | 4 +-- .../backup/KVMBackupExportServiceImpl.java | 28 +++++++++++-------- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java index e2016f76c1f..f3bd535a6b8 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java @@ -58,7 +58,7 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer ConfigKey BackupProviderPlugin = new ValidatedConfigKey<>("Advanced", String.class, "backup.framework.provider.plugin", "dummy", - "The backup and recovery provider plugin. Valid plugin values: dummy, veeam, networker, nas and veeam-kvm", + "The backup and recovery provider plugin. Valid plugin values: dummy, veeam, networker, nas", true, ConfigKey.Scope.Zone, BackupFrameworkEnabled.key(), value -> validateBackupProviderConfig((String)value)); ConfigKey BackupSyncPollingInterval = new ConfigKey<>("Advanced", Long.class, @@ -263,7 +263,7 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer if (value != null && (value.contains(",") || value.trim().contains(" "))) { throw new IllegalArgumentException("Multiple backup provider plugins are not supported. Please provide a single plugin value."); } - List validPlugins = List.of("dummy", "veeam", "networker", "nas", "veeam-kvm"); + List validPlugins = List.of("dummy", "veeam", "networker", "nas"); if (value != null && !validPlugins.contains(value)) { throw new IllegalArgumentException("Invalid backup provider plugin: " + value + ". Valid plugin values are: " + String.join(", ", validPlugins)); } diff --git a/api/src/main/java/org/apache/cloudstack/backup/KVMBackupExportService.java b/api/src/main/java/org/apache/cloudstack/backup/KVMBackupExportService.java index 6093293779b..7a53c1370c6 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/KVMBackupExportService.java +++ b/api/src/main/java/org/apache/cloudstack/backup/KVMBackupExportService.java @@ -34,7 +34,7 @@ import org.apache.cloudstack.framework.config.Configurable; import com.cloud.utils.component.PluggableService; /** - * Service for managing oVirt-style incremental backups using libvirt checkpoints + * Service for Creating Backups and ImageTransfer sessions which will be consumed by an external orchestrator. */ public interface KVMBackupExportService extends Configurable, PluggableService { @@ -43,7 +43,7 @@ public interface KVMBackupExportService extends Configurable, PluggableService { "10", "The image transfer progress polling interval in seconds.", true, ConfigKey.Scope.Global); - ConfigKey ExposeKVMBackupExportServiceApis = new ConfigKey<>("Hidden", Boolean.class, + ConfigKey ExposeKVMBackupExportServiceApis = new ConfigKey<>("Advanced", Boolean.class, "expose.kvm.backup.export.service.apis", "false", "Enable to expose APIs for testing the KVM Backup Export Service.", false, ConfigKey.Scope.Global); 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 37ae291107f..4594ca6301f 100644 --- a/server/src/main/java/org/apache/cloudstack/backup/KVMBackupExportServiceImpl.java +++ b/server/src/main/java/org/apache/cloudstack/backup/KVMBackupExportServiceImpl.java @@ -74,6 +74,7 @@ import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.dao.VMInstanceDao; +import static org.apache.cloudstack.backup.BackupManager.BackupFrameworkEnabled; import static org.apache.cloudstack.backup.BackupManager.BackupProviderPlugin; @Component @@ -105,6 +106,10 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup private Timer imageTransferTimer; + private boolean isKVMBackupExportServiceSupported(Long zoneId) { + return !BackupFrameworkEnabled.value() || StringUtils.equals("dummy", BackupProviderPlugin.valueIn(zoneId)); + } + @Override public Backup createBackup(StartBackupCmd cmd) { Long vmId = cmd.getVmId(); @@ -114,9 +119,9 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup throw new CloudRuntimeException("VM not found: " + vmId); } - if (!StringUtils.equals("veeam-kvm", BackupProviderPlugin.valueIn(vm.getDataCenterId()))) { - throw new CloudRuntimeException("Feature not enabled. Set Zone level config backup.framework.provider.plugin" + - " to \"veeam-kvm\" to enable the feature."); + if (!isKVMBackupExportServiceSupported(vm.getDataCenterId())) { + throw new CloudRuntimeException("Veeam-KVM integration can not be used along with the " + BackupProviderPlugin.valueIn(vm.getDataCenterId()) + + " backup provider. Either set backup.framework.enabled to false or set the Zone level config backup.framework.provider.plugin to \"dummy\"."); } if (vm.getState() != State.Running && vm.getState() != State.Stopped) { @@ -248,10 +253,9 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup List transfers = imageTransferDao.listByBackupId(backupId); for (ImageTransferVO transfer : transfers) { if (transfer.getPhase() != ImageTransferVO.Phase.finished) { - updateBackupState(backup, Backup.Status.Failed); - throw new CloudRuntimeException(String.format("Image transfer %s not finalized for backup: %s", transfer.getUuid(), backup.getUuid())); + logger.warn("Finalize called for backup {} while Image transfer {} is not finalized, attempting to finalize it", backup.getUuid(), transfer.getUuid()); + finalizeImageTransfer(transfer.getId()); } - imageTransferDao.remove(transfer.getId()); } if (vm.getState() == State.Running) { @@ -496,9 +500,9 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup throw new CloudRuntimeException("Volume not found with the specified Id"); } - if (!StringUtils.equals("veeam-kvm", BackupProviderPlugin.valueIn(volume.getDataCenterId()))) { - throw new CloudRuntimeException("Feature not enabled. Set Zone level config backup.framework.provider.plugin" + - " to \"veeam-kvm\" to enable the feature."); + if (!isKVMBackupExportServiceSupported(volume.getDataCenterId())) { + throw new CloudRuntimeException("Veeam-KVM integration can not be used along with the " + BackupProviderPlugin.valueIn(volume.getDataCenterId()) + + " backup provider. Either set backup.framework.enabled to false or set the Zone level config backup.framework.provider.plugin to \"dummy\"."); } ImageTransferVO existingTransfer = imageTransferDao.findUnfinishedByVolume(volume.getId()); @@ -665,9 +669,9 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup if (vm == null) { throw new CloudRuntimeException("VM not found: " + cmd.getVmId()); } - if (!StringUtils.equals("veeam-kvm", BackupProviderPlugin.valueIn(vm.getDataCenterId()))) { - throw new CloudRuntimeException("Feature not enabled. Set Zone level config backup.framework.provider.plugin" + - " to \"veeam-kvm\" to enable the feature."); + if (!isKVMBackupExportServiceSupported(vm.getDataCenterId())) { + throw new CloudRuntimeException("Veeam-KVM integration can not be used along with the " + BackupProviderPlugin.valueIn(vm.getDataCenterId()) + + " backup provider. Either set backup.framework.enabled to false or set the Zone level config backup.framework.provider.plugin to \"dummy\"."); } vm.setActiveCheckpointId(null);