Remove dependency on backup offering. Make backup export service exclusive to other backup providers.

This commit is contained in:
Abhisar Sinha 2026-03-28 17:47:39 +05:30 committed by Abhishek Kumar
parent b6d480cfb1
commit ca0ad93d61
4 changed files with 36 additions and 59 deletions

View File

@ -58,7 +58,7 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer
ConfigKey<String> BackupProviderPlugin = new ValidatedConfigKey<>("Advanced", String.class,
"backup.framework.provider.plugin",
"dummy",
"The backup and recovery provider plugin. Valid plugin values: dummy, veeam, networker and nas",
"The backup and recovery provider plugin. Valid plugin values: dummy, veeam, networker, nas and veeam-kvm",
true, ConfigKey.Scope.Zone, BackupFrameworkEnabled.key(), value -> validateBackupProviderConfig((String)value));
ConfigKey<Long> 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<String> validPlugins = List.of("dummy", "veeam", "networker", "nas");
List<String> validPlugins = List.of("dummy", "veeam", "networker", "nas", "veeam-kvm");
if (value != null && !validPlugins.contains(value)) {
throw new IllegalArgumentException("Invalid backup provider plugin: " + value + ". Valid plugin values are: " + String.join(", ", validPlugins));
}

View File

@ -120,7 +120,7 @@ CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.nics','enabled', 'TINYINT(1) NOT NUL
-- Add checkpoint tracking fields to backups table for incremental backup support
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'from_checkpoint_id', 'VARCHAR(255) DEFAULT NULL COMMENT "Previous active checkpoint id for incremental backups"');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'to_checkpoint_id', 'VARCHAR(255) DEFAULT NULL COMMENT "New checkpoint id created for this backup session"');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'to_checkpoint_id', 'VARCHAR(255) DEFAULT NULL COMMENT "New checkpoint id created for the next incremental backup"');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'checkpoint_create_time', 'BIGINT DEFAULT NULL COMMENT "Checkpoint creation timestamp from libvirt"');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'host_id', 'BIGINT UNSIGNED DEFAULT NULL COMMENT "Host where backup is running"');

View File

@ -2411,8 +2411,10 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
backedUpVolumes = new Gson().toJson(backup.getBackedUpVolumes().toArray(), Backup.VolumeInfo[].class);
}
response.setVolumes(backedUpVolumes);
response.setBackupOfferingId(offering.getUuid());
response.setBackupOffering(offering.getName());
if (offering != null) {
response.setBackupOfferingId(offering.getUuid());
response.setBackupOffering(offering.getName());
}
response.setAccountId(account.getUuid());
response.setAccount(account.getAccountName());
response.setDomainId(domain.getUuid());

View File

@ -41,7 +41,6 @@ import org.apache.cloudstack.api.command.admin.backup.StartBackupCmd;
import org.apache.cloudstack.api.response.CheckpointResponse;
import org.apache.cloudstack.api.response.ImageTransferResponse;
import org.apache.cloudstack.backup.dao.BackupDao;
import org.apache.cloudstack.backup.dao.BackupOfferingDao;
import org.apache.cloudstack.backup.dao.ImageTransferDao;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.managed.context.ManagedContextTimerTask;
@ -75,6 +74,8 @@ import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine.State;
import com.cloud.vm.dao.VMInstanceDao;
import static org.apache.cloudstack.backup.BackupManager.BackupProviderPlugin;
@Component
public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackupExportService {
@ -96,9 +97,6 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup
@Inject
private AgentManager agentManager;
@Inject
private BackupOfferingDao backupOfferingDao;
@Inject
private HostDao hostDao;
@ -107,20 +105,6 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup
private Timer imageTransferTimer;
private boolean isDummyOffering(Long backupOfferingId) {
if (backupOfferingId == null) {
throw new CloudRuntimeException("VM not assigned a backup offering");
}
BackupOfferingVO offering = backupOfferingDao.findById(backupOfferingId);
if (offering == null) {
throw new CloudRuntimeException("Backup offering not found: " + backupOfferingId);
}
if ("dummy".equalsIgnoreCase(offering.getName())) {
return true;
}
return false;
}
@Override
public Backup createBackup(StartBackupCmd cmd) {
Long vmId = cmd.getVmId();
@ -130,12 +114,13 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup
throw new CloudRuntimeException("VM not found: " + vmId);
}
if (vm.getState() != State.Running && vm.getState() != State.Stopped) {
throw new CloudRuntimeException("VM must be running or stopped to start backup");
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 (vm.getBackupOfferingId() == null) {
throw new CloudRuntimeException("VM not assigned a backup offering");
if (vm.getState() != State.Running && vm.getState() != State.Stopped) {
throw new CloudRuntimeException("VM must be running or stopped to start backup");
}
Backup existingBackup = backupDao.findByVmId(vmId);
@ -158,7 +143,7 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup
backup.setDomainId(vm.getDomainId());
backup.setZoneId(vm.getDataCenterId());
backup.setStatus(Backup.Status.Queued);
backup.setBackupOfferingId(vm.getBackupOfferingId());
backup.setBackupOfferingId(0L);
backup.setDate(new Date());
String toCheckpointId = "ckp-" + UUID.randomUUID().toString().substring(0, 8);
@ -175,7 +160,7 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup
return backupDao.persist(backup);
}
protected void removedFailedBackup(BackupVO backup) {
protected void removeFailedBackup(BackupVO backup) {
backup.setStatus(Backup.Status.Error);
backupDao.update(backup.getId(), backup);
backupDao.remove(backup.getId());
@ -208,23 +193,17 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup
vm.getState() == State.Stopped
);
boolean dummyOffering = isDummyOffering(vm.getBackupOfferingId());
StartBackupAnswer answer;
try {
if (dummyOffering) {
answer = new StartBackupAnswer(startCmd, true, "Dummy answer", System.currentTimeMillis());
} else {
answer = (StartBackupAnswer) agentManager.send(hostId, startCmd);
}
answer = (StartBackupAnswer) agentManager.send(hostId, startCmd);
} catch (AgentUnavailableException | OperationTimedoutException e) {
removedFailedBackup(backup);
removeFailedBackup(backup);
logger.error("Failed to communicate with agent on {} for {} start", host, backup, e);
throw new CloudRuntimeException("Failed to communicate with agent", e);
}
if (!answer.getResult()) {
removedFailedBackup(backup);
removeFailedBackup(backup);
logger.error("Failed to start {} due to: {}", backup, answer.getDetails());
throw new CloudRuntimeException("Failed to start backup: " + answer.getDetails());
}
@ -264,8 +243,6 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup
throw new CloudRuntimeException("VM not found: " + vmId);
}
boolean dummyOffering = isDummyOffering(backup.getBackupOfferingId());
updateBackupState(backup, Backup.Status.FinalizingTransfer);
List<ImageTransferVO> transfers = imageTransferDao.listByBackupId(backupId);
@ -282,12 +259,7 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup
StopBackupAnswer answer;
try {
if (dummyOffering) {
answer = new StopBackupAnswer(stopCmd, true, "Dummy answer");
} else {
answer = (StopBackupAnswer) agentManager.send(backup.getHostId(), stopCmd);
}
answer = (StopBackupAnswer) agentManager.send(backup.getHostId(), stopCmd);
} catch (AgentUnavailableException | OperationTimedoutException e) {
updateBackupState(backup, Backup.Status.Failed);
throw new CloudRuntimeException("Failed to communicate with agent", e);
@ -325,7 +297,6 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup
if (backup == null) {
throw new CloudRuntimeException("Backup not found: " + backupId);
}
boolean dummyOffering = isDummyOffering(backup.getBackupOfferingId());
if (ImageTransfer.Backend.file.equals(backend)) {
throw new CloudRuntimeException("File backend is not supported for download");
}
@ -349,11 +320,7 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup
try {
CreateImageTransferAnswer answer;
if (dummyOffering) {
answer = new CreateImageTransferAnswer(transferCmd, true, "Dummy answer", "image-transfer-id", "nbd://127.0.0.1:10809/vda");
} else {
answer = (CreateImageTransferAnswer) agentManager.send(backup.getHostId(), transferCmd);
}
answer = (CreateImageTransferAnswer) agentManager.send(backup.getHostId(), transferCmd);
if (!answer.getResult()) {
throw new CloudRuntimeException("Failed to create image transfer: " + answer.getDetails());
@ -525,6 +492,15 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup
ImageTransfer imageTransfer;
VolumeVO volume = volumeDao.findById(volumeId);
if (volume == null) {
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.");
}
ImageTransferVO existingTransfer = imageTransferDao.findUnfinishedByVolume(volume.getId());
if (existingTransfer != null) {
throw new CloudRuntimeException("Image transfer already in progress for volume: " + volume.getUuid());
@ -558,16 +534,10 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup
FinalizeImageTransferCommand finalizeCmd = new FinalizeImageTransferCommand(transferId);
BackupVO backup = backupDao.findById(imageTransfer.getBackupId());
boolean dummyOffering = isDummyOffering(backup.getBackupOfferingId());
Answer answer;
try {
if (dummyOffering) {
answer = new Answer(finalizeCmd, true, "Image transfer finalized.");
} else {
answer = agentManager.send(backup.getHostId(), finalizeCmd);
}
answer = agentManager.send(backup.getHostId(), finalizeCmd);
} catch (AgentUnavailableException | OperationTimedoutException e) {
throw new CloudRuntimeException("Failed to communicate with agent", e);
}
@ -695,6 +665,11 @@ 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.");
}
vm.setActiveCheckpointId(null);
vm.setActiveCheckpointCreateTime(null);
vmInstanceDao.update(cmd.getVmId(), vm);