From aeb07108fa1943ee39334c52b605dadf17b77c9d Mon Sep 17 00:00:00 2001 From: Daan Hoogland Date: Mon, 24 Feb 2025 20:52:04 +0100 Subject: [PATCH 1/5] resolve merge problems in the backup framework --- .../cloudstack/backup/BackupProvider.java | 7 ++ .../backup/DummyBackupProvider.java | 4 + .../cloudstack/backup/NASBackupProvider.java | 6 ++ .../backup/NetworkerBackupProvider.java | 89 ++++++++++++++++++- .../backup/VeeamBackupProvider.java | 27 +++++- 5 files changed, 131 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java b/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java index e3a6c3a62bd9..dd67a2478279 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java @@ -116,4 +116,11 @@ public interface BackupProvider { * @param metric */ Backup createNewBackupEntryForRestorePoint(Backup.RestorePoint restorePoint, VirtualMachine vm, Backup.Metric metric); + + /** + * This method should reconcile and create backup entries for any backups created out-of-band + * @param vm + * @param metric + */ + void syncBackups(VirtualMachine vm, Backup.Metric metric); } diff --git a/plugins/backup/dummy/src/main/java/org/apache/cloudstack/backup/DummyBackupProvider.java b/plugins/backup/dummy/src/main/java/org/apache/cloudstack/backup/DummyBackupProvider.java index 6935d177c72c..7de682b3eb35 100644 --- a/plugins/backup/dummy/src/main/java/org/apache/cloudstack/backup/DummyBackupProvider.java +++ b/plugins/backup/dummy/src/main/java/org/apache/cloudstack/backup/DummyBackupProvider.java @@ -146,4 +146,8 @@ public Pair takeBackup(VirtualMachine vm) { public boolean deleteBackup(Backup backup, boolean forced) { return true; } + + @Override + public void syncBackups(VirtualMachine vm, Backup.Metric metric) { + } } diff --git a/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java b/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java index f148c53e614d..3198f1a49251 100644 --- a/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java +++ b/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java @@ -36,6 +36,7 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VirtualMachine; import com.cloud.vm.dao.VMInstanceDao; + import org.apache.cloudstack.backup.dao.BackupDao; import org.apache.cloudstack.backup.dao.BackupOfferingDao; import org.apache.cloudstack.backup.dao.BackupRepositoryDao; @@ -414,6 +415,11 @@ public boolean willDeleteBackupsOnOfferingRemoval() { return false; } + @Override + public void syncBackups(VirtualMachine vm, Backup.Metric metric) { + // TODO: check and sum/return backups metrics on per VM basis + } + @Override public List listBackupOfferings(Long zoneId) { final List repositories = backupRepositoryDao.listByZoneAndProvider(zoneId, getName()); diff --git a/plugins/backup/networker/src/main/java/org/apache/cloudstack/backup/NetworkerBackupProvider.java b/plugins/backup/networker/src/main/java/org/apache/cloudstack/backup/NetworkerBackupProvider.java index 822688a86a35..0aaebf56701f 100644 --- a/plugins/backup/networker/src/main/java/org/apache/cloudstack/backup/NetworkerBackupProvider.java +++ b/plugins/backup/networker/src/main/java/org/apache/cloudstack/backup/NetworkerBackupProvider.java @@ -21,6 +21,7 @@ import com.cloud.host.Status; import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor; +import com.cloud.utils.script.Script; import com.cloud.storage.StoragePoolHostVO; import com.cloud.storage.Volume; import com.cloud.storage.VolumeVO; @@ -29,11 +30,16 @@ import com.cloud.utils.Pair; import com.cloud.utils.Ternary; import com.cloud.utils.component.AdapterBase; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallbackNoReturn; +import com.cloud.utils.db.TransactionStatus; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.ssh.SshHelper; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.dao.VMInstanceDao; + +import org.apache.cloudstack.api.InternalIdentity; import org.apache.cloudstack.backup.dao.BackupDao; import org.apache.cloudstack.backup.dao.BackupOfferingDaoImpl; import org.apache.cloudstack.backup.networker.NetworkerClient; @@ -44,7 +50,9 @@ import org.apache.logging.log4j.LogManager; import org.apache.xml.utils.URI; import org.apache.cloudstack.backup.networker.api.NetworkerBackup; + import javax.inject.Inject; + import java.net.URISyntaxException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; @@ -60,7 +68,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -import com.cloud.utils.script.Script; public class NetworkerBackupProvider extends AdapterBase implements BackupProvider, Configurable { @@ -560,6 +567,86 @@ public Map getBackupMetrics(Long zoneId, List backupsInDb = backupDao.listByVmId(null, vm.getId()); + final ArrayList backupsInNetworker = getClient(zoneId).getBackupsForVm(vm); + final List removeList = backupsInDb.stream().map(InternalIdentity::getId).collect(Collectors.toList()); + for (final String networkerBackupId : backupsInNetworker ) { + Long vmBackupSize=0L; + boolean backupExists = false; + for (final Backup backupInDb : backupsInDb) { + LOG.debug(String.format("Checking if Backup %s with external ID %s for VM %s is valid", backupsInDb, backupInDb.getName(), vm)); + if ( networkerBackupId.equals(backupInDb.getExternalId()) ) { + LOG.debug(String.format("Found Backup %s in both Database and Networker", backupInDb)); + backupExists = true; + removeList.remove(backupInDb.getId()); + if (metric != null) { + LOG.debug(String.format("Update backup [%s] from [size: %s, protected size: %s] to [size: %s, protected size: %s].", + backupInDb, backupInDb.getSize(), backupInDb.getProtectedSize(), + metric.getBackupSize(), metric.getDataSize())); + ((BackupVO) backupInDb).setSize(metric.getBackupSize()); + ((BackupVO) backupInDb).setProtectedSize(metric.getDataSize()); + backupDao.update(backupInDb.getId(), ((BackupVO) backupInDb)); + } + break; + } + } + if (backupExists) { + continue; + } + // Technically an administrator can manually create a backup for a VM by utilizing the KVM scripts + // with the proper parameters. So we will register any backups taken on the Networker side from + // outside Cloudstack. If ever Networker will support KVM out of the box this functionality also will + // ensure that SLA like backups will be found and registered. + NetworkerBackup strayNetworkerBackup = getClient(vm.getDataCenterId()).getNetworkerBackupInfo(networkerBackupId); + // Since running backups are already present in Networker Server but not completed + // make sure the backup is not in progress at this time. + if ( strayNetworkerBackup.getCompletionTime() != null) { + BackupVO strayBackup = new BackupVO(); + strayBackup.setVmId(vm.getId()); + strayBackup.setExternalId(strayNetworkerBackup.getId()); + strayBackup.setType(strayNetworkerBackup.getType()); + SimpleDateFormat formatterDateTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); + try { + strayBackup.setDate(formatterDateTime.parse(strayNetworkerBackup.getSaveTime())); + } catch (ParseException e) { + String msg = String.format("Unable to parse date [%s].", strayNetworkerBackup.getSaveTime()); + LOG.error(msg, e); + throw new CloudRuntimeException(msg, e); + } + strayBackup.setStatus(Backup.Status.BackedUp); + for ( Backup.VolumeInfo thisVMVol : vm.getBackupVolumeList()) { + vmBackupSize += (thisVMVol.getSize() / 1024L /1024L); + } + strayBackup.setSize(vmBackupSize); + strayBackup.setProtectedSize(strayNetworkerBackup.getSize().getValue() / 1024L ); + strayBackup.setBackupOfferingId(vm.getBackupOfferingId()); + strayBackup.setAccountId(vm.getAccountId()); + strayBackup.setDomainId(vm.getDomainId()); + strayBackup.setZoneId(vm.getDataCenterId()); + LOG.debug(String.format("Creating a new entry in backups: [id: %s, uuid: %s, vm_id: %s, external_id: %s, type: %s, date: %s, backup_offering_id: %s, account_id: %s, " + + "domain_id: %s, zone_id: %s].", strayBackup.getId(), strayBackup.getUuid(), strayBackup.getVmId(), strayBackup.getExternalId(), + strayBackup.getType(), strayBackup.getDate(), strayBackup.getBackupOfferingId(), strayBackup.getAccountId(), + strayBackup.getDomainId(), strayBackup.getZoneId())); + backupDao.persist(strayBackup); + LOG.warn("Added backup found in provider [" + strayBackup + "]"); + } else { + LOG.debug ("Backup is in progress, skipping addition for this run"); + } + } + for (final Long backupIdToRemove : removeList) { + LOG.warn(String.format("Removing backup with ID: [%s].", backupIdToRemove)); + backupDao.remove(backupIdToRemove); + } + } + }); + } + @Override public Backup createNewBackupEntryForRestorePoint(Backup.RestorePoint restorePoint, VirtualMachine vm, Backup.Metric metric) { // Technically an administrator can manually create a backup for a VM by utilizing the KVM scripts diff --git a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java index a7ce0c09cc61..579c707ec484 100644 --- a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java +++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java @@ -29,6 +29,8 @@ import javax.inject.Inject; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.InternalIdentity; import org.apache.cloudstack.backup.Backup.Metric; import org.apache.cloudstack.backup.dao.BackupDao; import org.apache.cloudstack.backup.veeam.VeeamClient; @@ -40,15 +42,21 @@ import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; +import com.cloud.event.ActionEventUtils; +import com.cloud.event.EventTypes; +import com.cloud.event.EventVO; import com.cloud.hypervisor.Hypervisor; import com.cloud.dc.VmwareDatacenter; -import com.cloud.hypervisor.vmware.VmwareDatacenterZoneMap; import com.cloud.dc.dao.VmwareDatacenterDao; +import com.cloud.hypervisor.vmware.VmwareDatacenterZoneMap; import com.cloud.hypervisor.vmware.dao.VmwareDatacenterZoneMapDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.user.User; import com.cloud.utils.Pair; import com.cloud.utils.component.AdapterBase; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallbackNoReturn; +import com.cloud.utils.db.TransactionStatus; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; @@ -344,6 +352,23 @@ public List listRestorePoints(VirtualMachine vm) { return getClient(vm.getDataCenterId()).listRestorePoints(backupName, vm.getInstanceName()); } + private Backup checkAndUpdateIfBackupEntryExistsForRestorePoint(List backupsInDb, Backup.RestorePoint restorePoint, Backup.Metric metric) { + for (final Backup backup : backupsInDb) { + if (restorePoint.getId().equals(backup.getExternalId())) { + if (metric != null) { + logger.debug("Update backup with [id: {}, uuid: {}, name: {}, external id: {}] from [size: {}, protected size: {}] to [size: {}, protected size: {}].", + backup.getId(), backup.getUuid(), backup.getName(), backup.getExternalId(), backup.getSize(), backup.getProtectedSize(), metric.getBackupSize(), metric.getDataSize()); + + ((BackupVO) backup).setSize(metric.getBackupSize()); + ((BackupVO) backup).setProtectedSize(metric.getDataSize()); + backupDao.update(backup.getId(), ((BackupVO) backup)); + } + return backup; + } + } + return null; + } + @Override public void syncBackups(VirtualMachine vm, Backup.Metric metric) { List restorePoints = listRestorePoints(vm); From f50dbd016d32042224c330c75731e7ed56be8021 Mon Sep 17 00:00:00 2001 From: Daan Hoogland Date: Mon, 24 Feb 2025 21:10:12 +0100 Subject: [PATCH 2/5] some cleanups while waiting for compile --- .../cloudstack/backup/BackupProvider.java | 7 ++-- .../backup/DummyBackupProvider.java | 6 +-- .../cloudstack/backup/NASBackupProvider.java | 20 +++------ .../backup/NetworkerBackupProvider.java | 42 +++++++++---------- 4 files changed, 32 insertions(+), 43 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java b/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java index dd67a2478279..5a47a9ca25e2 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java @@ -50,8 +50,7 @@ public interface BackupProvider { /** * Assign a VM to a backup offering or policy * @param vm - * @param backup - * @param policy + * @param backupOffering * @return */ boolean assignVMToBackupOffering(VirtualMachine vm, BackupOffering backupOffering); @@ -72,14 +71,14 @@ public interface BackupProvider { /** * Starts and creates an adhoc backup process * for a previously registered VM backup - * @param backup + * @param vm * @return */ Pair takeBackup(VirtualMachine vm); /** * Delete an existing backup - * @param backuo The backup to exclude + * @param backup The backup to exclude * @param forced Indicates if backup will be force removed or not * @return */ diff --git a/plugins/backup/dummy/src/main/java/org/apache/cloudstack/backup/DummyBackupProvider.java b/plugins/backup/dummy/src/main/java/org/apache/cloudstack/backup/DummyBackupProvider.java index 7de682b3eb35..9997f7d8987f 100644 --- a/plugins/backup/dummy/src/main/java/org/apache/cloudstack/backup/DummyBackupProvider.java +++ b/plugins/backup/dummy/src/main/java/org/apache/cloudstack/backup/DummyBackupProvider.java @@ -112,7 +112,7 @@ public Backup createNewBackupEntryForRestorePoint(Backup.RestorePoint restorePoi @Override public boolean removeVMFromBackupOffering(VirtualMachine vm) { - logger.debug(String.format("Removing VM %s from backup offering by the Dummy Backup Provider", vm)); + logger.debug("Removing VM {} from backup offering by the Dummy Backup Provider", vm); return true; } @@ -123,7 +123,7 @@ public boolean willDeleteBackupsOnOfferingRemoval() { @Override public Pair takeBackup(VirtualMachine vm) { - logger.debug(String.format("Starting backup for VM %s on Dummy provider", vm)); + logger.debug("Starting backup for VM {} on Dummy provider", vm); BackupVO backup = new BackupVO(); backup.setVmId(vm.getId()); @@ -131,7 +131,7 @@ public Pair takeBackup(VirtualMachine vm) { backup.setType("FULL"); backup.setDate(new Date()); backup.setSize(1024000L); - backup.setProtectedSize(1 * Resource.ResourceType.bytesToGiB); + backup.setProtectedSize(Resource.ResourceType.bytesToGiB); backup.setStatus(Backup.Status.BackedUp); backup.setBackupOfferingId(vm.getBackupOfferingId()); backup.setAccountId(vm.getAccountId()); diff --git a/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java b/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java index 3198f1a49251..340474a835b8 100644 --- a/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java +++ b/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java @@ -17,7 +17,6 @@ package org.apache.cloudstack.backup; import com.cloud.agent.AgentManager; -import com.cloud.dc.dao.ClusterDao; import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.OperationTimedoutException; import com.cloud.host.Host; @@ -38,7 +37,6 @@ import com.cloud.vm.dao.VMInstanceDao; import org.apache.cloudstack.backup.dao.BackupDao; -import org.apache.cloudstack.backup.dao.BackupOfferingDao; import org.apache.cloudstack.backup.dao.BackupRepositoryDao; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; @@ -71,15 +69,9 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co @Inject private BackupRepositoryDao backupRepositoryDao; - @Inject - private BackupOfferingDao backupOfferingDao; - @Inject private HostDao hostDao; - @Inject - private ClusterDao clusterDao; - @Inject private VolumeDao volumeDao; @@ -167,7 +159,7 @@ public Pair takeBackup(final VirtualMachine vm) { command.setVolumePaths(volumePaths); } - BackupAnswer answer = null; + BackupAnswer answer; try { answer = (BackupAnswer) agentManager.send(host.getId(), command); } catch (AgentUnavailableException e) { @@ -205,7 +197,7 @@ private BackupVO createBackupObject(VirtualMachine vm, String backupPath) { virtualSize += volume.getSize(); } } - backup.setProtectedSize(Long.valueOf(virtualSize)); + backup.setProtectedSize(virtualSize); backup.setStatus(Backup.Status.BackingUp); backup.setBackupOfferingId(vm.getBackupOfferingId()); backup.setAccountId(vm.getAccountId()); @@ -232,7 +224,7 @@ public boolean restoreVMFromBackup(VirtualMachine vm, Backup backup) { restoreCommand.setVmExists(vm.getRemoved() == null); restoreCommand.setVmState(vm.getState()); - BackupAnswer answer = null; + BackupAnswer answer; try { answer = (BackupAnswer) agentManager.send(host.getId(), restoreCommand); } catch (AgentUnavailableException e) { @@ -299,7 +291,7 @@ public Pair restoreBackedUpVolume(Backup backup, String volumeU restoreCommand.setVmState(vmNameAndState.second()); restoreCommand.setRestoreVolumeUUID(volumeUuid); - BackupAnswer answer = null; + BackupAnswer answer; try { answer = (BackupAnswer) agentManager.send(hostVO.getId(), restoreCommand); } catch (AgentUnavailableException e) { @@ -351,7 +343,7 @@ public boolean deleteBackup(Backup backup, boolean forced) { DeleteBackupCommand command = new DeleteBackupCommand(backup.getExternalId(), backupRepository.getType(), backupRepository.getAddress(), backupRepository.getMountOptions()); - BackupAnswer answer = null; + BackupAnswer answer; try { answer = (BackupAnswer) agentManager.send(host.getId(), command); } catch (AgentUnavailableException e) { @@ -364,7 +356,7 @@ public boolean deleteBackup(Backup backup, boolean forced) { return backupDao.remove(backup.getId()); } - logger.debug("There was an error removing the backup with id " + backup.getId()); + logger.debug("There was an error removing the backup with id {}", backup.getId()); return false; } diff --git a/plugins/backup/networker/src/main/java/org/apache/cloudstack/backup/NetworkerBackupProvider.java b/plugins/backup/networker/src/main/java/org/apache/cloudstack/backup/NetworkerBackupProvider.java index 0aaebf56701f..5adbf43b8c5d 100644 --- a/plugins/backup/networker/src/main/java/org/apache/cloudstack/backup/NetworkerBackupProvider.java +++ b/plugins/backup/networker/src/main/java/org/apache/cloudstack/backup/NetworkerBackupProvider.java @@ -176,7 +176,7 @@ protected HostVO getLastVMHypervisorHost(VirtualMachine vm) { List altClusterHosts = hostDao.findHypervisorHostInCluster(host.getClusterId()); for (final HostVO candidateClusterHost : altClusterHosts) { if ( candidateClusterHost.getStatus() == Status.Up ) { - LOG.debug(String.format("Found Host %s", candidateClusterHost)); + LOG.debug("Found Host {}", candidateClusterHost); return candidateClusterHost; } } @@ -185,7 +185,7 @@ protected HostVO getLastVMHypervisorHost(VirtualMachine vm) { List altZoneHosts = hostDao.findByDataCenterId(host.getDataCenterId()); for (final HostVO candidateZoneHost : altZoneHosts) { if ( candidateZoneHost.getStatus() == Status.Up && candidateZoneHost.getHypervisorType() == Hypervisor.HypervisorType.KVM ) { - LOG.debug("Found Host " + candidateZoneHost); + LOG.debug("Found Host {}", candidateZoneHost); return candidateZoneHost; } } @@ -236,13 +236,13 @@ private String executeBackupCommand(HostVO host, String username, String passwor Pair response = SshHelper.sshExecute(host.getPrivateIpAddress(), 22, username, null, password, command, 120000, 120000, 3600000); if (!response.first()) { - LOG.error(String.format("Backup Script failed on HYPERVISOR %s due to: %s", host, response.second())); + LOG.error("Backup Script failed on HYPERVISOR {} due to: {}", host, response.second()); } else { - LOG.debug(String.format("Networker Backup Results: %s", response.second())); + LOG.debug("Networker Backup Results: {}", response.second()); } Matcher saveTimeMatcher = saveTimePattern.matcher(response.second()); if (saveTimeMatcher.find()) { - LOG.debug(String.format("Got saveTimeMatcher: %s", saveTimeMatcher.group(1))); + LOG.debug("Got saveTimeMatcher: {}", saveTimeMatcher.group(1)); return saveTimeMatcher.group(1); } } catch (final Exception e) { @@ -258,9 +258,9 @@ private boolean executeRestoreCommand(HostVO host, String username, String passw username, null, password, command, 120000, 120000, 3600000); if (!response.first()) { - LOG.error(String.format("Restore Script failed on HYPERVISOR %s due to: %s", host, response.second())); + LOG.error("Restore Script failed on HYPERVISOR {} due to: {}", host, response.second()); } else { - LOG.debug(String.format("Networker Restore Results: %s",response.second())); + LOG.debug("Networker Restore Results: {}",response.second()); return true; } } catch (final Exception e) { @@ -317,7 +317,7 @@ public boolean removeVMFromBackupOffering(VirtualMachine vm) { List backupsTaken = getClient(vm.getDataCenterId()).getBackupsForVm(vm); for (String backupId : backupsTaken) { - LOG.debug("Trying to remove backup with id" + backupId); + LOG.debug("Trying to remove backup with id {}", backupId); getClient(vm.getDataCenterId()).deleteBackupForVM(backupId); } @@ -334,10 +334,10 @@ public boolean restoreVMFromBackup(VirtualMachine vm, Backup backup) { final NetworkerBackup networkerBackup=getClient(zoneId).getNetworkerBackupInfo(externalBackupId); final String SSID = networkerBackup.getShortId(); - LOG.debug(String.format("Restoring vm %s from backup %s on the Networker Backup Provider", vm, backup)); + LOG.debug("Restoring vm {} from backup {} on the Networker Backup Provider", vm, backup); if ( SSID.isEmpty() ) { - LOG.debug("There was an error retrieving the SSID for backup with id " + externalBackupId + " from EMC NEtworker"); + LOG.debug("There was an error retrieving the SSID for backup with id {} from EMC NEtworker", externalBackupId); return false; } @@ -345,7 +345,7 @@ public boolean restoreVMFromBackup(VirtualMachine vm, Backup backup) { hostVO = getLastVMHypervisorHost(vm); // Get credentials for that host Ternary credentials = getKVMHyperisorCredentials(hostVO); - LOG.debug("The SSID was reported successfully " + externalBackupId); + LOG.debug("The SSID was reported successfully {}", externalBackupId); try { networkerServer = getUrlDomain(NetworkerUrl.value()); } catch (URISyntaxException e) { @@ -362,14 +362,14 @@ public boolean restoreVMFromBackup(VirtualMachine vm, Backup backup) { script.add("-v"); Date restoreJobStart = new Date(); - LOG.debug(String.format("Starting Restore for VM %s and %s at %s", vm, SSID, restoreJobStart)); + LOG.debug("Starting Restore for VM {} and {} at {}", vm, SSID, restoreJobStart); if ( executeRestoreCommand(hostVO, credentials.first(), credentials.second(), script.toString()) ) { Date restoreJobEnd = new Date(); - LOG.debug("Restore Job for SSID " + SSID + " completed successfully at " + restoreJobEnd); + LOG.debug("Restore Job for SSID {} completed successfully at {}", SSID, restoreJobEnd); return true; } else { - LOG.debug("Restore Job for SSID " + SSID + " failed!"); + LOG.debug("Restore Job for SSID {} failed!", SSID); return false; } } @@ -390,7 +390,7 @@ public Pair restoreBackedUpVolume(Backup backup, String volumeU final String destinationNetworkerClient = hostVO.getName().split("\\.")[0]; Long restoredVolumeDiskSize = 0L; - LOG.debug(String.format("Restoring volume %s with uuid %s from backup %s on the Networker Backup Provider", volume, volumeUuid, backup)); + LOG.debug("Restoring volume {} with uuid {} from backup {} on the Networker Backup Provider", volume, volumeUuid, backup); if ( SSID.isEmpty() ) { LOG.debug("There was an error retrieving the SSID for backup with id " + externalBackupId + " from EMC NEtworker"); @@ -543,8 +543,8 @@ public boolean deleteBackup(Backup backup, boolean forced) { @Override public Map getBackupMetrics(Long zoneId, List vms) { final Map metrics = new HashMap<>(); - Long vmBackupSize=0L; - Long vmBackupProtectedSize=0L; + long vmBackupSize=0L; + long vmBackupProtectedSize=0L; if (CollectionUtils.isEmpty(vms)) { LOG.warn("Unable to get VM Backup Metrics because the list of VMs is empty."); @@ -577,7 +577,7 @@ public void doInTransactionWithoutResult(TransactionStatus status) { final ArrayList backupsInNetworker = getClient(zoneId).getBackupsForVm(vm); final List removeList = backupsInDb.stream().map(InternalIdentity::getId).collect(Collectors.toList()); for (final String networkerBackupId : backupsInNetworker ) { - Long vmBackupSize=0L; + long vmBackupSize=0L; boolean backupExists = false; for (final Backup backupInDb : backupsInDb) { LOG.debug(String.format("Checking if Backup %s with external ID %s for VM %s is valid", backupsInDb, backupInDb.getName(), vm)); @@ -671,7 +671,7 @@ public Backup createNewBackupEntryForRestorePoint(Backup.RestorePoint restorePoi throw new CloudRuntimeException(msg, e); } backup.setStatus(Backup.Status.BackedUp); - Long vmBackupProtectedSize=0L; + long vmBackupProtectedSize=0L; for (Backup.VolumeInfo thisVMVol : vm.getBackupVolumeList()) { vmBackupProtectedSize += (thisVMVol.getSize() / 1024L / 1024L); } @@ -692,9 +692,7 @@ public Backup createNewBackupEntryForRestorePoint(Backup.RestorePoint restorePoi public List listRestorePoints(VirtualMachine vm) { final Long zoneId = vm.getDataCenterId(); final ArrayList backupIds = getClient(zoneId).getBackupsForVm(vm); - List restorePoints = - backupIds.stream().map(id -> new Backup.RestorePoint(id, null, null)).collect(Collectors.toList()); - return restorePoints; + return backupIds.stream().map(id -> new Backup.RestorePoint(id, null, null)).collect(Collectors.toList()); } @Override From daeb86c67fdd5c183d6ad6ac223b328351977ac4 Mon Sep 17 00:00:00 2001 From: Daan Hoogland Date: Tue, 25 Feb 2025 09:44:35 +0100 Subject: [PATCH 3/5] re-remove sync method (and do some warning cleanups) --- .../cloudstack/backup/BackupProvider.java | 41 +++---- .../backup/DummyBackupProvider.java | 4 - .../cloudstack/backup/NASBackupProvider.java | 9 +- .../backup/NetworkerBackupProvider.java | 84 -------------- .../backup/VeeamBackupProvider.java | 108 ++++++++---------- 5 files changed, 64 insertions(+), 182 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java b/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java index 5a47a9ca25e2..39582b0e423c 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java @@ -49,21 +49,21 @@ public interface BackupProvider { /** * Assign a VM to a backup offering or policy - * @param vm - * @param backupOffering - * @return + * @param vm the machine to back up + * @param backupOffering the SLA definition for the backup + * @return succeeded? */ boolean assignVMToBackupOffering(VirtualMachine vm, BackupOffering backupOffering); /** * Removes a VM from a backup offering or policy - * @param vm - * @return + * @param vm the machine to stop backing up + * @return succeeded? */ boolean removeVMFromBackupOffering(VirtualMachine vm); /** - * Whether the provide will delete backups on removal of VM from the offfering + * Whether the provider will delete backups on removal of VM from the offering * @return boolean result */ boolean willDeleteBackupsOnOfferingRemoval(); @@ -71,8 +71,8 @@ public interface BackupProvider { /** * Starts and creates an adhoc backup process * for a previously registered VM backup - * @param vm - * @return + * @param vm the machine to make a backup of + * @return the result and {code}Backup{code} {code}Object{code} */ Pair takeBackup(VirtualMachine vm); @@ -80,7 +80,7 @@ public interface BackupProvider { * Delete an existing backup * @param backup The backup to exclude * @param forced Indicates if backup will be force removed or not - * @return + * @return succeeded? */ boolean deleteBackup(Backup backup, boolean forced); @@ -96,30 +96,23 @@ public interface BackupProvider { /** * Returns backup metrics for a list of VMs in a zone - * @param zoneId - * @param vms - * @return + * @param zoneId the zone for which to return metrics + * @param vms a list of machines to get measurements for + * @return a map of machine -> backup metrics */ Map getBackupMetrics(Long zoneId, List vms); /** * This method should TODO - * @param + * @param vm the machine to get restore point for */ - public List listRestorePoints(VirtualMachine vm); + List listRestorePoints(VirtualMachine vm); /** * This method should TODO - * @param - * @param - * @param metric + * @param restorePoint the restore point to create a backup for + * @param vm The machine for which to create a backup + * @param metric the metric object to update with the new backup data */ Backup createNewBackupEntryForRestorePoint(Backup.RestorePoint restorePoint, VirtualMachine vm, Backup.Metric metric); - - /** - * This method should reconcile and create backup entries for any backups created out-of-band - * @param vm - * @param metric - */ - void syncBackups(VirtualMachine vm, Backup.Metric metric); } diff --git a/plugins/backup/dummy/src/main/java/org/apache/cloudstack/backup/DummyBackupProvider.java b/plugins/backup/dummy/src/main/java/org/apache/cloudstack/backup/DummyBackupProvider.java index 9997f7d8987f..2d43f9e8d3c8 100644 --- a/plugins/backup/dummy/src/main/java/org/apache/cloudstack/backup/DummyBackupProvider.java +++ b/plugins/backup/dummy/src/main/java/org/apache/cloudstack/backup/DummyBackupProvider.java @@ -146,8 +146,4 @@ public Pair takeBackup(VirtualMachine vm) { public boolean deleteBackup(Backup backup, boolean forced) { return true; } - - @Override - public void syncBackups(VirtualMachine vm, Backup.Metric metric) { - } } diff --git a/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java b/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java index 340474a835b8..ba37c8991697 100644 --- a/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java +++ b/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java @@ -101,7 +101,7 @@ protected Host getLastVMHypervisorHost(VirtualMachine vm) { // Try to find any Up host in the same cluster for (final Host hostInCluster : hostDao.findHypervisorHostInCluster(host.getClusterId())) { if (hostInCluster.getStatus() == Status.Up) { - LOG.debug("Found Host {}", hostInCluster); + LOG.debug("Found Host {} in cluster {}", hostInCluster, host.getClusterId()); return hostInCluster; } } @@ -109,7 +109,7 @@ protected Host getLastVMHypervisorHost(VirtualMachine vm) { // Try to find any Host in the zone for (final HostVO hostInZone : hostDao.listByDataCenterIdAndHypervisorType(host.getDataCenterId(), Hypervisor.HypervisorType.KVM)) { if (hostInZone.getStatus() == Status.Up) { - LOG.debug("Found Host {}", hostInZone); + LOG.debug("Found Host {} in zone {}", hostInZone, host.getDataCenterId()); return hostInZone; } } @@ -407,11 +407,6 @@ public boolean willDeleteBackupsOnOfferingRemoval() { return false; } - @Override - public void syncBackups(VirtualMachine vm, Backup.Metric metric) { - // TODO: check and sum/return backups metrics on per VM basis - } - @Override public List listBackupOfferings(Long zoneId) { final List repositories = backupRepositoryDao.listByZoneAndProvider(zoneId, getName()); diff --git a/plugins/backup/networker/src/main/java/org/apache/cloudstack/backup/NetworkerBackupProvider.java b/plugins/backup/networker/src/main/java/org/apache/cloudstack/backup/NetworkerBackupProvider.java index 5adbf43b8c5d..504a551bb30b 100644 --- a/plugins/backup/networker/src/main/java/org/apache/cloudstack/backup/NetworkerBackupProvider.java +++ b/plugins/backup/networker/src/main/java/org/apache/cloudstack/backup/NetworkerBackupProvider.java @@ -30,16 +30,12 @@ import com.cloud.utils.Pair; import com.cloud.utils.Ternary; import com.cloud.utils.component.AdapterBase; -import com.cloud.utils.db.Transaction; -import com.cloud.utils.db.TransactionCallbackNoReturn; -import com.cloud.utils.db.TransactionStatus; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.ssh.SshHelper; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.dao.VMInstanceDao; -import org.apache.cloudstack.api.InternalIdentity; import org.apache.cloudstack.backup.dao.BackupDao; import org.apache.cloudstack.backup.dao.BackupOfferingDaoImpl; import org.apache.cloudstack.backup.networker.NetworkerClient; @@ -567,86 +563,6 @@ public Map getBackupMetrics(Long zoneId, List backupsInDb = backupDao.listByVmId(null, vm.getId()); - final ArrayList backupsInNetworker = getClient(zoneId).getBackupsForVm(vm); - final List removeList = backupsInDb.stream().map(InternalIdentity::getId).collect(Collectors.toList()); - for (final String networkerBackupId : backupsInNetworker ) { - long vmBackupSize=0L; - boolean backupExists = false; - for (final Backup backupInDb : backupsInDb) { - LOG.debug(String.format("Checking if Backup %s with external ID %s for VM %s is valid", backupsInDb, backupInDb.getName(), vm)); - if ( networkerBackupId.equals(backupInDb.getExternalId()) ) { - LOG.debug(String.format("Found Backup %s in both Database and Networker", backupInDb)); - backupExists = true; - removeList.remove(backupInDb.getId()); - if (metric != null) { - LOG.debug(String.format("Update backup [%s] from [size: %s, protected size: %s] to [size: %s, protected size: %s].", - backupInDb, backupInDb.getSize(), backupInDb.getProtectedSize(), - metric.getBackupSize(), metric.getDataSize())); - ((BackupVO) backupInDb).setSize(metric.getBackupSize()); - ((BackupVO) backupInDb).setProtectedSize(metric.getDataSize()); - backupDao.update(backupInDb.getId(), ((BackupVO) backupInDb)); - } - break; - } - } - if (backupExists) { - continue; - } - // Technically an administrator can manually create a backup for a VM by utilizing the KVM scripts - // with the proper parameters. So we will register any backups taken on the Networker side from - // outside Cloudstack. If ever Networker will support KVM out of the box this functionality also will - // ensure that SLA like backups will be found and registered. - NetworkerBackup strayNetworkerBackup = getClient(vm.getDataCenterId()).getNetworkerBackupInfo(networkerBackupId); - // Since running backups are already present in Networker Server but not completed - // make sure the backup is not in progress at this time. - if ( strayNetworkerBackup.getCompletionTime() != null) { - BackupVO strayBackup = new BackupVO(); - strayBackup.setVmId(vm.getId()); - strayBackup.setExternalId(strayNetworkerBackup.getId()); - strayBackup.setType(strayNetworkerBackup.getType()); - SimpleDateFormat formatterDateTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); - try { - strayBackup.setDate(formatterDateTime.parse(strayNetworkerBackup.getSaveTime())); - } catch (ParseException e) { - String msg = String.format("Unable to parse date [%s].", strayNetworkerBackup.getSaveTime()); - LOG.error(msg, e); - throw new CloudRuntimeException(msg, e); - } - strayBackup.setStatus(Backup.Status.BackedUp); - for ( Backup.VolumeInfo thisVMVol : vm.getBackupVolumeList()) { - vmBackupSize += (thisVMVol.getSize() / 1024L /1024L); - } - strayBackup.setSize(vmBackupSize); - strayBackup.setProtectedSize(strayNetworkerBackup.getSize().getValue() / 1024L ); - strayBackup.setBackupOfferingId(vm.getBackupOfferingId()); - strayBackup.setAccountId(vm.getAccountId()); - strayBackup.setDomainId(vm.getDomainId()); - strayBackup.setZoneId(vm.getDataCenterId()); - LOG.debug(String.format("Creating a new entry in backups: [id: %s, uuid: %s, vm_id: %s, external_id: %s, type: %s, date: %s, backup_offering_id: %s, account_id: %s, " - + "domain_id: %s, zone_id: %s].", strayBackup.getId(), strayBackup.getUuid(), strayBackup.getVmId(), strayBackup.getExternalId(), - strayBackup.getType(), strayBackup.getDate(), strayBackup.getBackupOfferingId(), strayBackup.getAccountId(), - strayBackup.getDomainId(), strayBackup.getZoneId())); - backupDao.persist(strayBackup); - LOG.warn("Added backup found in provider [" + strayBackup + "]"); - } else { - LOG.debug ("Backup is in progress, skipping addition for this run"); - } - } - for (final Long backupIdToRemove : removeList) { - LOG.warn(String.format("Removing backup with ID: [%s].", backupIdToRemove)); - backupDao.remove(backupIdToRemove); - } - } - }); - } - @Override public Backup createNewBackupEntryForRestorePoint(Backup.RestorePoint restorePoint, VirtualMachine vm, Backup.Metric metric) { // Technically an administrator can manually create a backup for a VM by utilizing the KVM scripts diff --git a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java index 579c707ec484..2b71687fc0ff 100644 --- a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java +++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java @@ -75,27 +75,27 @@ public class VeeamBackupProvider extends AdapterBase implements BackupProvider, "backup.plugin.veeam.version", "0", "The version of Veeam backup and recovery. CloudStack will get Veeam server version via PowerShell commands if it is 0 or not set", true, ConfigKey.Scope.Zone); - private ConfigKey VeeamUsername = new ConfigKey<>("Advanced", String.class, + private final ConfigKey VeeamUsername = new ConfigKey<>("Advanced", String.class, "backup.plugin.veeam.username", "administrator", "The Veeam backup and recovery username.", true, ConfigKey.Scope.Zone); - private ConfigKey VeeamPassword = new ConfigKey<>("Secure", String.class, + private final ConfigKey VeeamPassword = new ConfigKey<>("Secure", String.class, "backup.plugin.veeam.password", "", "The Veeam backup and recovery password.", true, ConfigKey.Scope.Zone); - private ConfigKey VeeamValidateSSLSecurity = new ConfigKey<>("Advanced", Boolean.class, "backup.plugin.veeam.validate.ssl", "false", + private final ConfigKey VeeamValidateSSLSecurity = new ConfigKey<>("Advanced", Boolean.class, "backup.plugin.veeam.validate.ssl", "false", "When set to true, this will validate the SSL certificate when connecting to https/ssl enabled Veeam API service.", true, ConfigKey.Scope.Zone); - private ConfigKey VeeamApiRequestTimeout = new ConfigKey<>("Advanced", Integer.class, "backup.plugin.veeam.request.timeout", "300", + private final ConfigKey VeeamApiRequestTimeout = new ConfigKey<>("Advanced", Integer.class, "backup.plugin.veeam.request.timeout", "300", "The Veeam B&R API request timeout in seconds.", true, ConfigKey.Scope.Zone); - private static ConfigKey VeeamRestoreTimeout = new ConfigKey<>("Advanced", Integer.class, "backup.plugin.veeam.restore.timeout", "600", + private static final ConfigKey VeeamRestoreTimeout = new ConfigKey<>("Advanced", Integer.class, "backup.plugin.veeam.restore.timeout", "600", "The Veeam B&R API restore backup timeout in seconds.", true, ConfigKey.Scope.Zone); - private static ConfigKey VeeamTaskPollInterval = new ConfigKey<>("Advanced", Integer.class, "backup.plugin.veeam.task.poll.interval", "5", + private static final ConfigKey VeeamTaskPollInterval = new ConfigKey<>("Advanced", Integer.class, "backup.plugin.veeam.task.poll.interval", "5", "The time interval in seconds when the management server polls for Veeam task status.", true, ConfigKey.Scope.Zone); - private static ConfigKey VeeamTaskPollMaxRetry = new ConfigKey<>("Advanced", Integer.class, "backup.plugin.veeam.task.poll.max.retry", "120", + private static final ConfigKey VeeamTaskPollMaxRetry = new ConfigKey<>("Advanced", Integer.class, "backup.plugin.veeam.task.poll.max.retry", "120", "The max number of retrying times when the management server polls for Veeam task status.", true, ConfigKey.Scope.Zone); @Inject @@ -202,7 +202,7 @@ public boolean removeVMFromBackupOffering(final VirtualMachine vm) { final VmwareDatacenter vmwareDC = findVmwareDatacenterForVM(vm); try { if (!client.removeVMFromVeeamJob(vm.getBackupExternalId(), vm.getInstanceName(), vmwareDC.getVcenterHost())) { - logger.warn("Failed to remove VM from Veeam Job id: " + vm.getBackupExternalId()); + logger.warn("Failed to remove VM from Veeam Job id: {}", vm.getBackupExternalId()); } } catch (Exception e) { logger.debug("VM was removed from the job so could not remove again, trying to delete the veeam job now.", e); @@ -210,7 +210,7 @@ public boolean removeVMFromBackupOffering(final VirtualMachine vm) { final String clonedJobName = getGuestBackupName(vm.getInstanceName(), vm.getUuid()); if (!client.deleteJobAndBackup(clonedJobName)) { - logger.warn("Failed to remove Veeam job and backup for job: " + clonedJobName); + logger.warn("Failed to remove Veeam job and backup for job: {}", clonedJobName); throw new CloudRuntimeException("Failed to delete Veeam B&R job and backup, an operation may be in progress. Please try again after some time."); } client.syncBackupRepository(); @@ -236,9 +236,9 @@ public boolean deleteBackup(Backup backup, boolean forced) { throw new CloudRuntimeException(String.format("Could not find any VM associated with the Backup [uuid: %s, name: %s, externalId: %s].", backup.getUuid(), backup.getName(), backup.getExternalId())); } if (!forced) { - logger.debug(String.format("Veeam backup provider does not have a safe way to remove a single restore point, which results in all backup chain being removed. " - + "More information about this limitation can be found in the links: [%s, %s].", "https://forums.veeam.com/powershell-f26/removing-a-single-restorepoint-t21061.html", - "https://helpcenter.veeam.com/docs/backup/vsphere/retention_separate_vms.html?ver=110")); + logger.debug("Veeam backup provider does not have a safe way to remove a single restore point, which results in all backup chain being removed. " + + "More information about this limitation can be found in the links: [{}, {}].", "https://forums.veeam.com/powershell-f26/removing-a-single-restorepoint-t21061.html", + "https://helpcenter.veeam.com/docs/backup/vsphere/retention_separate_vms.html?ver=110"); throw new CloudRuntimeException("Veeam backup provider does not have a safe way to remove a single restore point, which results in all backup chain being removed. " + "Use forced:true to skip this verification and remove the complete backup chain."); } @@ -265,7 +265,7 @@ public boolean restoreVMFromBackup(VirtualMachine vm, Backup backup) { try { return getClient(vm.getDataCenterId()).restoreFullVM(vm.getInstanceName(), restorePointId); } catch (Exception ex) { - logger.error(String.format("Failed to restore Full VM due to: %s. Retrying after some preparation", ex.getMessage())); + logger.error("Failed to restore Full VM due to: {}. Retrying after some preparation", ex.getMessage()); prepareForBackupRestoration(vm); return getClient(vm.getDataCenterId()).restoreFullVM(vm.getInstanceName(), restorePointId); } @@ -275,7 +275,7 @@ private void prepareForBackupRestoration(VirtualMachine vm) { if (!Hypervisor.HypervisorType.VMware.equals(vm.getHypervisorType())) { return; } - logger.info("Preparing for restoring VM " + vm); + logger.info("Preparing for restoring VM {}", vm); PrepareForBackupRestorationCommand command = new PrepareForBackupRestorationCommand(vm.getInstanceName()); Long hostId = virtualMachineManager.findClusterAndHostIdForVm(vm.getId()).second(); if (hostId == null) { @@ -284,7 +284,7 @@ private void prepareForBackupRestoration(VirtualMachine vm) { try { Answer answer = agentMgr.easySend(hostId, command); if (answer != null && answer.getResult()) { - logger.info("Succeeded to prepare for restoring VM " + vm); + logger.info("Succeeded to prepare for restoring VM {}", vm); } else { throw new CloudRuntimeException(String.format("Failed to prepare for restoring VM %s. details: %s", vm, (answer != null ? answer.getDetails() : null))); @@ -310,7 +310,7 @@ public Map getBackupMetrics(final Long zoneId, fi } List vmUuids = vms.stream().filter(Objects::nonNull).map(VirtualMachine::getUuid).collect(Collectors.toList()); - logger.debug(String.format("Get Backup Metrics for VMs: [%s].", String.join(", ", vmUuids))); + logger.debug("Get Backup Metrics for VMs: [{}].", String.join(", ", vmUuids)); final Map backendMetrics = getClient(zoneId).getBackupMetrics(); for (final VirtualMachine vm : vms) { @@ -328,54 +328,12 @@ public Map getBackupMetrics(final Long zoneId, fi @Override public Backup createNewBackupEntryForRestorePoint(Backup.RestorePoint restorePoint, VirtualMachine vm, Backup.Metric metric) { - BackupVO backup = new BackupVO(); - backup.setVmId(vm.getId()); - backup.setExternalId(restorePoint.getId()); - backup.setType(restorePoint.getType()); - backup.setDate(restorePoint.getCreated()); - backup.setStatus(Backup.Status.BackedUp); - if (metric != null) { - backup.setSize(metric.getBackupSize()); - backup.setProtectedSize(metric.getDataSize()); - } - backup.setBackupOfferingId(vm.getBackupOfferingId()); - backup.setAccountId(vm.getAccountId()); - backup.setDomainId(vm.getDomainId()); - backup.setZoneId(vm.getDataCenterId()); - backupDao.persist(backup); - return backup; - } - - @Override - public List listRestorePoints(VirtualMachine vm) { - String backupName = getGuestBackupName(vm.getInstanceName(), vm.getUuid()); - return getClient(vm.getDataCenterId()).listRestorePoints(backupName, vm.getInstanceName()); - } - - private Backup checkAndUpdateIfBackupEntryExistsForRestorePoint(List backupsInDb, Backup.RestorePoint restorePoint, Backup.Metric metric) { - for (final Backup backup : backupsInDb) { - if (restorePoint.getId().equals(backup.getExternalId())) { - if (metric != null) { - logger.debug("Update backup with [id: {}, uuid: {}, name: {}, external id: {}] from [size: {}, protected size: {}] to [size: {}, protected size: {}].", - backup.getId(), backup.getUuid(), backup.getName(), backup.getExternalId(), backup.getSize(), backup.getProtectedSize(), metric.getBackupSize(), metric.getDataSize()); - - ((BackupVO) backup).setSize(metric.getBackupSize()); - ((BackupVO) backup).setProtectedSize(metric.getDataSize()); - backupDao.update(backup.getId(), ((BackupVO) backup)); - } - return backup; - } - } - return null; - } - - @Override - public void syncBackups(VirtualMachine vm, Backup.Metric metric) { List restorePoints = listRestorePoints(vm); if (CollectionUtils.isEmpty(restorePoints)) { logger.debug("Can't find any restore point to VM: {}", vm); - return; + return null; } + final Backup[] persistedBackup = new Backup[1]; Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { @@ -407,19 +365,43 @@ public void doInTransactionWithoutResult(TransactionStatus status) { logger.debug("Creating a new entry in backups: [id: {}, uuid: {}, name: {}, vm_id: {}, external_id: {}, type: {}, date: {}, backup_offering_id: {}, account_id: {}, " + "domain_id: {}, zone_id: {}].", backup.getId(), backup.getUuid(), backup.getName(), backup.getVmId(), backup.getExternalId(), backup.getType(), backup.getDate(), backup.getBackupOfferingId(), backup.getAccountId(), backup.getDomainId(), backup.getZoneId()); - backupDao.persist(backup); + persistedBackup[0] = backupDao.persist(backup); ActionEventUtils.onCompletedActionEvent(User.UID_SYSTEM, vm.getAccountId(), EventVO.LEVEL_INFO, EventTypes.EVENT_VM_BACKUP_CREATE, - String.format("Created backup %s for VM ID: %s", backup.getUuid(), vm.getUuid()), + String.format("Created backup %s for VM ID: %s", persistedBackup[0].getUuid(), vm.getUuid()), vm.getId(), ApiCommandResourceType.VirtualMachine.toString(),0); } } for (final Long backupIdToRemove : removeList) { - logger.warn(String.format("Removing backup with ID: [%s].", backupIdToRemove)); + logger.warn("Removing backup with ID: [{}].", backupIdToRemove); backupDao.remove(backupIdToRemove); } } }); + return persistedBackup[0]; + } + + @Override + public List listRestorePoints(VirtualMachine vm) { + String backupName = getGuestBackupName(vm.getInstanceName(), vm.getUuid()); + return getClient(vm.getDataCenterId()).listRestorePoints(backupName, vm.getInstanceName()); + } + + private Backup checkAndUpdateIfBackupEntryExistsForRestorePoint(List backupsInDb, Backup.RestorePoint restorePoint, Backup.Metric metric) { + for (final Backup backup : backupsInDb) { + if (restorePoint.getId().equals(backup.getExternalId())) { + if (metric != null) { + logger.debug("Update backup with [id: {}, uuid: {}, name: {}, external id: {}] from [size: {}, protected size: {}] to [size: {}, protected size: {}].", + backup.getId(), backup.getUuid(), backup.getName(), backup.getExternalId(), backup.getSize(), backup.getProtectedSize(), metric.getBackupSize(), metric.getDataSize()); + + ((BackupVO) backup).setSize(metric.getBackupSize()); + ((BackupVO) backup).setProtectedSize(metric.getDataSize()); + backupDao.update(backup.getId(), ((BackupVO) backup)); + } + return backup; + } + } + return null; } @Override From 2cc0ecdfa21667a37e22ea77d4b95aee19a46f30 Mon Sep 17 00:00:00 2001 From: Daan Hoogland Date: Tue, 25 Feb 2025 13:30:10 +0100 Subject: [PATCH 4/5] revert plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java --- .../backup/VeeamBackupProvider.java | 128 +++++------------- 1 file changed, 32 insertions(+), 96 deletions(-) diff --git a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java index 2b71687fc0ff..0735136d15d8 100644 --- a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java +++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java @@ -29,8 +29,6 @@ import javax.inject.Inject; -import org.apache.cloudstack.api.ApiCommandResourceType; -import org.apache.cloudstack.api.InternalIdentity; import org.apache.cloudstack.backup.Backup.Metric; import org.apache.cloudstack.backup.dao.BackupDao; import org.apache.cloudstack.backup.veeam.VeeamClient; @@ -42,21 +40,13 @@ import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; -import com.cloud.event.ActionEventUtils; -import com.cloud.event.EventTypes; -import com.cloud.event.EventVO; import com.cloud.hypervisor.Hypervisor; import com.cloud.dc.VmwareDatacenter; -import com.cloud.dc.dao.VmwareDatacenterDao; import com.cloud.hypervisor.vmware.VmwareDatacenterZoneMap; +import com.cloud.dc.dao.VmwareDatacenterDao; import com.cloud.hypervisor.vmware.dao.VmwareDatacenterZoneMapDao; -import com.cloud.storage.dao.VolumeDao; -import com.cloud.user.User; import com.cloud.utils.Pair; import com.cloud.utils.component.AdapterBase; -import com.cloud.utils.db.Transaction; -import com.cloud.utils.db.TransactionCallbackNoReturn; -import com.cloud.utils.db.TransactionStatus; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; @@ -75,27 +65,27 @@ public class VeeamBackupProvider extends AdapterBase implements BackupProvider, "backup.plugin.veeam.version", "0", "The version of Veeam backup and recovery. CloudStack will get Veeam server version via PowerShell commands if it is 0 or not set", true, ConfigKey.Scope.Zone); - private final ConfigKey VeeamUsername = new ConfigKey<>("Advanced", String.class, + private ConfigKey VeeamUsername = new ConfigKey<>("Advanced", String.class, "backup.plugin.veeam.username", "administrator", "The Veeam backup and recovery username.", true, ConfigKey.Scope.Zone); - private final ConfigKey VeeamPassword = new ConfigKey<>("Secure", String.class, + private ConfigKey VeeamPassword = new ConfigKey<>("Secure", String.class, "backup.plugin.veeam.password", "", "The Veeam backup and recovery password.", true, ConfigKey.Scope.Zone); - private final ConfigKey VeeamValidateSSLSecurity = new ConfigKey<>("Advanced", Boolean.class, "backup.plugin.veeam.validate.ssl", "false", + private ConfigKey VeeamValidateSSLSecurity = new ConfigKey<>("Advanced", Boolean.class, "backup.plugin.veeam.validate.ssl", "false", "When set to true, this will validate the SSL certificate when connecting to https/ssl enabled Veeam API service.", true, ConfigKey.Scope.Zone); - private final ConfigKey VeeamApiRequestTimeout = new ConfigKey<>("Advanced", Integer.class, "backup.plugin.veeam.request.timeout", "300", + private ConfigKey VeeamApiRequestTimeout = new ConfigKey<>("Advanced", Integer.class, "backup.plugin.veeam.request.timeout", "300", "The Veeam B&R API request timeout in seconds.", true, ConfigKey.Scope.Zone); - private static final ConfigKey VeeamRestoreTimeout = new ConfigKey<>("Advanced", Integer.class, "backup.plugin.veeam.restore.timeout", "600", + private static ConfigKey VeeamRestoreTimeout = new ConfigKey<>("Advanced", Integer.class, "backup.plugin.veeam.restore.timeout", "600", "The Veeam B&R API restore backup timeout in seconds.", true, ConfigKey.Scope.Zone); - private static final ConfigKey VeeamTaskPollInterval = new ConfigKey<>("Advanced", Integer.class, "backup.plugin.veeam.task.poll.interval", "5", + private static ConfigKey VeeamTaskPollInterval = new ConfigKey<>("Advanced", Integer.class, "backup.plugin.veeam.task.poll.interval", "5", "The time interval in seconds when the management server polls for Veeam task status.", true, ConfigKey.Scope.Zone); - private static final ConfigKey VeeamTaskPollMaxRetry = new ConfigKey<>("Advanced", Integer.class, "backup.plugin.veeam.task.poll.max.retry", "120", + private static ConfigKey VeeamTaskPollMaxRetry = new ConfigKey<>("Advanced", Integer.class, "backup.plugin.veeam.task.poll.max.retry", "120", "The max number of retrying times when the management server polls for Veeam task status.", true, ConfigKey.Scope.Zone); @Inject @@ -110,8 +100,6 @@ public class VeeamBackupProvider extends AdapterBase implements BackupProvider, private AgentManager agentMgr; @Inject private VirtualMachineManager virtualMachineManager; - @Inject - private VolumeDao volumeDao; protected VeeamClient getClient(final Long zoneId) { try { @@ -202,7 +190,7 @@ public boolean removeVMFromBackupOffering(final VirtualMachine vm) { final VmwareDatacenter vmwareDC = findVmwareDatacenterForVM(vm); try { if (!client.removeVMFromVeeamJob(vm.getBackupExternalId(), vm.getInstanceName(), vmwareDC.getVcenterHost())) { - logger.warn("Failed to remove VM from Veeam Job id: {}", vm.getBackupExternalId()); + logger.warn("Failed to remove VM from Veeam Job id: " + vm.getBackupExternalId()); } } catch (Exception e) { logger.debug("VM was removed from the job so could not remove again, trying to delete the veeam job now.", e); @@ -210,7 +198,7 @@ public boolean removeVMFromBackupOffering(final VirtualMachine vm) { final String clonedJobName = getGuestBackupName(vm.getInstanceName(), vm.getUuid()); if (!client.deleteJobAndBackup(clonedJobName)) { - logger.warn("Failed to remove Veeam job and backup for job: {}", clonedJobName); + logger.warn("Failed to remove Veeam job and backup for job: " + clonedJobName); throw new CloudRuntimeException("Failed to delete Veeam B&R job and backup, an operation may be in progress. Please try again after some time."); } client.syncBackupRepository(); @@ -236,9 +224,9 @@ public boolean deleteBackup(Backup backup, boolean forced) { throw new CloudRuntimeException(String.format("Could not find any VM associated with the Backup [uuid: %s, name: %s, externalId: %s].", backup.getUuid(), backup.getName(), backup.getExternalId())); } if (!forced) { - logger.debug("Veeam backup provider does not have a safe way to remove a single restore point, which results in all backup chain being removed. " - + "More information about this limitation can be found in the links: [{}, {}].", "https://forums.veeam.com/powershell-f26/removing-a-single-restorepoint-t21061.html", - "https://helpcenter.veeam.com/docs/backup/vsphere/retention_separate_vms.html?ver=110"); + logger.debug(String.format("Veeam backup provider does not have a safe way to remove a single restore point, which results in all backup chain being removed. " + + "More information about this limitation can be found in the links: [%s, %s].", "https://forums.veeam.com/powershell-f26/removing-a-single-restorepoint-t21061.html", + "https://helpcenter.veeam.com/docs/backup/vsphere/retention_separate_vms.html?ver=110")); throw new CloudRuntimeException("Veeam backup provider does not have a safe way to remove a single restore point, which results in all backup chain being removed. " + "Use forced:true to skip this verification and remove the complete backup chain."); } @@ -265,7 +253,7 @@ public boolean restoreVMFromBackup(VirtualMachine vm, Backup backup) { try { return getClient(vm.getDataCenterId()).restoreFullVM(vm.getInstanceName(), restorePointId); } catch (Exception ex) { - logger.error("Failed to restore Full VM due to: {}. Retrying after some preparation", ex.getMessage()); + logger.error(String.format("Failed to restore Full VM due to: %s. Retrying after some preparation", ex.getMessage())); prepareForBackupRestoration(vm); return getClient(vm.getDataCenterId()).restoreFullVM(vm.getInstanceName(), restorePointId); } @@ -275,7 +263,7 @@ private void prepareForBackupRestoration(VirtualMachine vm) { if (!Hypervisor.HypervisorType.VMware.equals(vm.getHypervisorType())) { return; } - logger.info("Preparing for restoring VM {}", vm); + logger.info("Preparing for restoring VM " + vm); PrepareForBackupRestorationCommand command = new PrepareForBackupRestorationCommand(vm.getInstanceName()); Long hostId = virtualMachineManager.findClusterAndHostIdForVm(vm.getId()).second(); if (hostId == null) { @@ -284,7 +272,7 @@ private void prepareForBackupRestoration(VirtualMachine vm) { try { Answer answer = agentMgr.easySend(hostId, command); if (answer != null && answer.getResult()) { - logger.info("Succeeded to prepare for restoring VM {}", vm); + logger.info("Succeeded to prepare for restoring VM " + vm); } else { throw new CloudRuntimeException(String.format("Failed to prepare for restoring VM %s. details: %s", vm, (answer != null ? answer.getDetails() : null))); @@ -310,7 +298,7 @@ public Map getBackupMetrics(final Long zoneId, fi } List vmUuids = vms.stream().filter(Objects::nonNull).map(VirtualMachine::getUuid).collect(Collectors.toList()); - logger.debug("Get Backup Metrics for VMs: [{}].", String.join(", ", vmUuids)); + logger.debug(String.format("Get Backup Metrics for VMs: [%s].", String.join(", ", vmUuids))); final Map backendMetrics = getClient(zoneId).getBackupMetrics(); for (final VirtualMachine vm : vms) { @@ -328,57 +316,22 @@ public Map getBackupMetrics(final Long zoneId, fi @Override public Backup createNewBackupEntryForRestorePoint(Backup.RestorePoint restorePoint, VirtualMachine vm, Backup.Metric metric) { - List restorePoints = listRestorePoints(vm); - if (CollectionUtils.isEmpty(restorePoints)) { - logger.debug("Can't find any restore point to VM: {}", vm); - return null; + BackupVO backup = new BackupVO(); + backup.setVmId(vm.getId()); + backup.setExternalId(restorePoint.getId()); + backup.setType(restorePoint.getType()); + backup.setDate(restorePoint.getCreated()); + backup.setStatus(Backup.Status.BackedUp); + if (metric != null) { + backup.setSize(metric.getBackupSize()); + backup.setProtectedSize(metric.getDataSize()); } - final Backup[] persistedBackup = new Backup[1]; - Transaction.execute(new TransactionCallbackNoReturn() { - @Override - public void doInTransactionWithoutResult(TransactionStatus status) { - final List backupsInDb = backupDao.listByVmId(null, vm.getId()); - final List removeList = backupsInDb.stream().map(InternalIdentity::getId).collect(Collectors.toList()); - for (final Backup.RestorePoint restorePoint : restorePoints) { - if (!(restorePoint.getId() == null || restorePoint.getType() == null || restorePoint.getCreated() == null)) { - Backup existingBackupEntry = checkAndUpdateIfBackupEntryExistsForRestorePoint(backupsInDb, restorePoint, metric); - if (existingBackupEntry != null) { - removeList.remove(existingBackupEntry.getId()); - continue; - } - - BackupVO backup = new BackupVO(); - backup.setVmId(vm.getId()); - backup.setExternalId(restorePoint.getId()); - backup.setType(restorePoint.getType()); - backup.setDate(restorePoint.getCreated()); - backup.setStatus(Backup.Status.BackedUp); - if (metric != null) { - backup.setSize(metric.getBackupSize()); - backup.setProtectedSize(metric.getDataSize()); - } - backup.setBackupOfferingId(vm.getBackupOfferingId()); - backup.setAccountId(vm.getAccountId()); - backup.setDomainId(vm.getDomainId()); - backup.setZoneId(vm.getDataCenterId()); - backup.setBackedUpVolumes(BackupManagerImpl.createVolumeInfoFromVolumes(volumeDao.findByInstance(vm.getId()))); - - logger.debug("Creating a new entry in backups: [id: {}, uuid: {}, name: {}, vm_id: {}, external_id: {}, type: {}, date: {}, backup_offering_id: {}, account_id: {}, " - + "domain_id: {}, zone_id: {}].", backup.getId(), backup.getUuid(), backup.getName(), backup.getVmId(), backup.getExternalId(), backup.getType(), backup.getDate(), backup.getBackupOfferingId(), backup.getAccountId(), backup.getDomainId(), backup.getZoneId()); - persistedBackup[0] = backupDao.persist(backup); - - ActionEventUtils.onCompletedActionEvent(User.UID_SYSTEM, vm.getAccountId(), EventVO.LEVEL_INFO, EventTypes.EVENT_VM_BACKUP_CREATE, - String.format("Created backup %s for VM ID: %s", persistedBackup[0].getUuid(), vm.getUuid()), - vm.getId(), ApiCommandResourceType.VirtualMachine.toString(),0); - } - } - for (final Long backupIdToRemove : removeList) { - logger.warn("Removing backup with ID: [{}].", backupIdToRemove); - backupDao.remove(backupIdToRemove); - } - } - }); - return persistedBackup[0]; + backup.setBackupOfferingId(vm.getBackupOfferingId()); + backup.setAccountId(vm.getAccountId()); + backup.setDomainId(vm.getDomainId()); + backup.setZoneId(vm.getDataCenterId()); + backupDao.persist(backup); + return backup; } @Override @@ -387,23 +340,6 @@ public List listRestorePoints(VirtualMachine vm) { return getClient(vm.getDataCenterId()).listRestorePoints(backupName, vm.getInstanceName()); } - private Backup checkAndUpdateIfBackupEntryExistsForRestorePoint(List backupsInDb, Backup.RestorePoint restorePoint, Backup.Metric metric) { - for (final Backup backup : backupsInDb) { - if (restorePoint.getId().equals(backup.getExternalId())) { - if (metric != null) { - logger.debug("Update backup with [id: {}, uuid: {}, name: {}, external id: {}] from [size: {}, protected size: {}] to [size: {}, protected size: {}].", - backup.getId(), backup.getUuid(), backup.getName(), backup.getExternalId(), backup.getSize(), backup.getProtectedSize(), metric.getBackupSize(), metric.getDataSize()); - - ((BackupVO) backup).setSize(metric.getBackupSize()); - ((BackupVO) backup).setProtectedSize(metric.getDataSize()); - backupDao.update(backup.getId(), ((BackupVO) backup)); - } - return backup; - } - } - return null; - } - @Override public String getConfigComponentName() { return BackupService.class.getSimpleName(); From 6da5db5120a8c1b9de90bc2b2956e296af4acd5b Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Tue, 18 Feb 2025 17:31:15 +0100 Subject: [PATCH 5/5] Veeam: set backed_volumes for each backup (#9898) --- .../org/apache/cloudstack/backup/VeeamBackupProvider.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java index 0735136d15d8..0a013587f6fd 100644 --- a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java +++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java @@ -45,6 +45,7 @@ import com.cloud.hypervisor.vmware.VmwareDatacenterZoneMap; import com.cloud.dc.dao.VmwareDatacenterDao; import com.cloud.hypervisor.vmware.dao.VmwareDatacenterZoneMapDao; +import com.cloud.storage.dao.VolumeDao; import com.cloud.utils.Pair; import com.cloud.utils.component.AdapterBase; import com.cloud.utils.exception.CloudRuntimeException; @@ -100,6 +101,8 @@ public class VeeamBackupProvider extends AdapterBase implements BackupProvider, private AgentManager agentMgr; @Inject private VirtualMachineManager virtualMachineManager; + @Inject + private VolumeDao volumeDao; protected VeeamClient getClient(final Long zoneId) { try { @@ -330,6 +333,7 @@ public Backup createNewBackupEntryForRestorePoint(Backup.RestorePoint restorePoi backup.setAccountId(vm.getAccountId()); backup.setDomainId(vm.getDomainId()); backup.setZoneId(vm.getDataCenterId()); + backup.setBackedUpVolumes(BackupManagerImpl.createVolumeInfoFromVolumes(volumeDao.findByInstance(vm.getId()))); backupDao.persist(backup); return backup; }