diff --git a/core/src/main/java/org/apache/cloudstack/backup/CommvaultTakeBackupCommand.java b/core/src/main/java/org/apache/cloudstack/backup/CommvaultTakeBackupCommand.java index aab5600adf31..9b0cf5c77e27 100644 --- a/core/src/main/java/org/apache/cloudstack/backup/CommvaultTakeBackupCommand.java +++ b/core/src/main/java/org/apache/cloudstack/backup/CommvaultTakeBackupCommand.java @@ -29,6 +29,7 @@ public class CommvaultTakeBackupCommand extends Command { private String backupPath; private List volumePools; private List volumePaths; + private List volumeUuids; private Boolean quiesce; public CommvaultTakeBackupCommand(String vmName, String backupPath) { @@ -69,6 +70,14 @@ public void setVolumePaths(List volumePaths) { this.volumePaths = volumePaths; } + public List getVolumeUuids() { + return volumeUuids; + } + + public void setVolumeUuids(List volumeUuids) { + this.volumeUuids = volumeUuids; + } + public Boolean getQuiesce() { return quiesce; } @@ -81,4 +90,4 @@ public void setQuiesce(Boolean quiesce) { public boolean executeInSequence() { return true; } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/TakeBackupCommand.java b/core/src/main/java/org/apache/cloudstack/backup/TakeBackupCommand.java index 5402b6b24760..bdd6773444f3 100644 --- a/core/src/main/java/org/apache/cloudstack/backup/TakeBackupCommand.java +++ b/core/src/main/java/org/apache/cloudstack/backup/TakeBackupCommand.java @@ -32,6 +32,7 @@ public class TakeBackupCommand extends Command { private String backupRepoAddress; private List volumePools; private List volumePaths; + private List volumeUuids; private Boolean quiesce; @LogLevel(LogLevel.Log4jLevel.Off) private String mountOptions; @@ -98,6 +99,14 @@ public void setVolumePaths(List volumePaths) { this.volumePaths = volumePaths; } + public List getVolumeUuids() { + return volumeUuids; + } + + public void setVolumeUuids(List volumeUuids) { + this.volumeUuids = volumeUuids; + } + public Boolean getQuiesce() { return quiesce; } diff --git a/plugins/backup/commvault/src/main/java/org/apache/cloudstack/backup/CommvaultBackupProvider.java b/plugins/backup/commvault/src/main/java/org/apache/cloudstack/backup/CommvaultBackupProvider.java index 57cf988a3ae2..f0483704c786 100644 --- a/plugins/backup/commvault/src/main/java/org/apache/cloudstack/backup/CommvaultBackupProvider.java +++ b/plugins/backup/commvault/src/main/java/org/apache/cloudstack/backup/CommvaultBackupProvider.java @@ -300,10 +300,11 @@ public Pair takeBackup(VirtualMachine vm, Boolean quiesceVM) { BackupVO backupVO = createBackupObject(vm, backupPath); CommvaultTakeBackupCommand command = new CommvaultTakeBackupCommand(vm.getInstanceName(), backupPath); command.setQuiesce(quiesceVM); + List vmVolumes = volumeDao.findByInstance(vm.getId()); + vmVolumes.sort(Comparator.comparing(Volume::getDeviceId)); + command.setVolumeUuids(vmVolumes.stream().map(VolumeVO::getUuid).collect(Collectors.toList())); if (VirtualMachine.State.Stopped.equals(vm.getState())) { - List vmVolumes = volumeDao.findByInstance(vm.getId()); - vmVolumes.sort(Comparator.comparing(Volume::getDeviceId)); Pair, List> volumePoolsAndPaths = getVolumePoolsAndPaths(vmVolumes); command.setVolumePools(volumePoolsAndPaths.first()); command.setVolumePaths(volumePoolsAndPaths.second()); 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 d081e3502c60..7d6b6be0d8f8 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 @@ -219,10 +219,11 @@ public Pair takeBackup(final VirtualMachine vm, Boolean quiesce command.setBackupRepoAddress(backupRepository.getAddress()); command.setMountOptions(backupRepository.getMountOptions()); command.setQuiesce(quiesceVM); + List vmVolumes = volumeDao.findByInstance(vm.getId()); + vmVolumes.sort(Comparator.comparing(Volume::getDeviceId)); + command.setVolumeUuids(vmVolumes.stream().map(VolumeVO::getUuid).collect(Collectors.toList())); if (VirtualMachine.State.Stopped.equals(vm.getState())) { - List vmVolumes = volumeDao.findByInstance(vm.getId()); - vmVolumes.sort(Comparator.comparing(Volume::getDeviceId)); Pair, List> volumePoolsAndPaths = getVolumePoolsAndPaths(vmVolumes); command.setVolumePools(volumePoolsAndPaths.first()); command.setVolumePaths(volumePoolsAndPaths.second()); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCommvaultTakeBackupCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCommvaultTakeBackupCommandWrapper.java index b40fa464e5b1..3a1533fc7e9a 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCommvaultTakeBackupCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCommvaultTakeBackupCommandWrapper.java @@ -46,6 +46,7 @@ public Answer execute(CommvaultTakeBackupCommand command, LibvirtComputingResour final String backupPath = command.getBackupPath(); List volumePools = command.getVolumePools(); final List volumePaths = command.getVolumePaths(); + final List volumeUuids = command.getVolumeUuids(); KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr(); List diskPaths = new ArrayList<>(); @@ -70,7 +71,8 @@ public Answer execute(CommvaultTakeBackupCommand command, LibvirtComputingResour "-v", vmName, "-p", backupPath, "-q", command.getQuiesce() != null && command.getQuiesce() ? "true" : "false", - "-d", diskPaths.isEmpty() ? "" : String.join(",", diskPaths) + "-d", diskPaths.isEmpty() ? "" : String.join(",", diskPaths), + "-u", volumeUuids == null || volumeUuids.isEmpty() ? "" : String.join(",", volumeUuids) }); Pair result = Script.executePipedCommands(commands, libvirtComputingResource.getCmdsTimeout()); @@ -88,4 +90,4 @@ public Answer execute(CommvaultTakeBackupCommand command, LibvirtComputingResour BackupAnswer answer = new BackupAnswer(command, true, "success"); return answer; } -} \ No newline at end of file +} diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtTakeBackupCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtTakeBackupCommandWrapper.java index 907e99a16215..dce67c7647cb 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtTakeBackupCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtTakeBackupCommandWrapper.java @@ -51,6 +51,7 @@ public Answer execute(TakeBackupCommand command, LibvirtComputingResource libvir final String mountOptions = command.getMountOptions(); List volumePools = command.getVolumePools(); final List volumePaths = command.getVolumePaths(); + final List volumeUuids = command.getVolumeUuids(); KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr(); List diskPaths = new ArrayList<>(); @@ -78,7 +79,8 @@ public Answer execute(TakeBackupCommand command, LibvirtComputingResource libvir "-m", Objects.nonNull(mountOptions) ? mountOptions : "", "-p", backupPath, "-q", command.getQuiesce() != null && command.getQuiesce() ? "true" : "false", - "-d", diskPaths.isEmpty() ? "" : String.join(",", diskPaths) + "-d", diskPaths.isEmpty() ? "" : String.join(",", diskPaths), + "-u", volumeUuids == null || volumeUuids.isEmpty() ? "" : String.join(",", volumeUuids) }); Pair result = Script.executePipedCommands(commands, libvirtComputingResource.getCmdsTimeout()); @@ -110,4 +112,4 @@ public Answer execute(TakeBackupCommand command, LibvirtComputingResource libvir answer.setSize(backupSize); return answer; } -} \ No newline at end of file +} diff --git a/scripts/vm/hypervisor/kvm/cvtbackup.sh b/scripts/vm/hypervisor/kvm/cvtbackup.sh index e00594312846..557260e0d3d5 100644 --- a/scripts/vm/hypervisor/kvm/cvtbackup.sh +++ b/scripts/vm/hypervisor/kvm/cvtbackup.sh @@ -29,6 +29,7 @@ OP="" VM="" BACKUP_DIR="" DISK_PATHS="" +VOLUME_UUIDS="" QUIESCE="" logFile="/var/log/cloudstack/agent/agent.log" @@ -90,12 +91,23 @@ sanity_checks() { backup_running_vm() { mkdir -p "$dest" || { echo "Failed to create backup directory $dest"; exit 1; } + local -a volume_uuid_arr=() + if [[ -n "$VOLUME_UUIDS" ]]; then + read -r -a volume_uuid_arr <<< "${VOLUME_UUIDS//,/ }" + fi + name="root" + local disk_index=0 echo "" > $dest/backup.xml for disk in $(virsh -c qemu:///system domblklist $VM --details 2>/dev/null | awk '/disk/{print$3}'); do volpath=$(virsh -c qemu:///system domblklist $VM --details | awk "/$disk/{print $4}" | sed 's/.*\///') - echo "" >> $dest/backup.xml + volid="$volpath" + if [[ ${#volume_uuid_arr[@]} -gt $disk_index && -n "${volume_uuid_arr[$disk_index]}" ]]; then + volid="${volume_uuid_arr[$disk_index]}" + fi + echo "" >> $dest/backup.xml name="datadisk" + ((disk_index+=1)) done echo "" >> $dest/backup.xml @@ -151,10 +163,17 @@ backup_stopped_vm() { mkdir -p "$dest" || { echo "Failed to create backup directory $dest"; exit 1; } IFS="," + local -a volume_uuid_arr=() + if [[ -n "$VOLUME_UUIDS" ]]; then + IFS=',' read -r -a volume_uuid_arr <<< "$VOLUME_UUIDS" + fi name="root" + local disk_index=0 for disk in $DISK_PATHS; do - if [[ "$disk" == rbd:* ]]; then + if [[ ${#volume_uuid_arr[@]} -gt $disk_index && -n "${volume_uuid_arr[$disk_index]}" ]]; then + volUuid="${volume_uuid_arr[$disk_index]}" + elif [[ "$disk" == rbd:* ]]; then # disk for rbd => rbd:/:mon_host=... # sample: rbd:cloudstack/53d5c355-d726-4d3e-9422-046a503a0b12:mon_host=10.0.1.2... beforeUuid="${disk#*/}" # Remove up to first slash after rbd: @@ -168,6 +187,7 @@ backup_stopped_vm() { cleanup fi name="datadisk" + ((disk_index+=1)) done sync @@ -218,6 +238,11 @@ while [[ $# -gt 0 ]]; do shift shift ;; + -u|--volumeuuids) + VOLUME_UUIDS="$2" + shift + shift + ;; -h|--help) usage shift @@ -252,4 +277,4 @@ else backup_stopped_vm fi -exit 0 \ No newline at end of file +exit 0 diff --git a/scripts/vm/hypervisor/kvm/nasbackup.sh b/scripts/vm/hypervisor/kvm/nasbackup.sh index 64a74f7689aa..2edad978d02e 100755 --- a/scripts/vm/hypervisor/kvm/nasbackup.sh +++ b/scripts/vm/hypervisor/kvm/nasbackup.sh @@ -31,6 +31,7 @@ NAS_ADDRESS="" MOUNT_OPTS="" BACKUP_DIR="" DISK_PATHS="" +VOLUME_UUIDS="" QUIESCE="" logFile="/var/log/cloudstack/agent/agent.log" @@ -93,12 +94,23 @@ backup_running_vm() { mount_operation mkdir -p "$dest" || { echo "Failed to create backup directory $dest"; exit 1; } + local -a volume_uuid_arr=() + if [[ -n "$VOLUME_UUIDS" ]]; then + read -r -a volume_uuid_arr <<< "${VOLUME_UUIDS//,/ }" + fi + name="root" + local disk_index=0 echo "" > $dest/backup.xml for disk in $(virsh -c qemu:///system domblklist $VM --details 2>/dev/null | awk '/disk/{print$3}'); do volpath=$(virsh -c qemu:///system domblklist $VM --details | awk "/$disk/{print $4}" | sed 's/.*\///') - echo "" >> $dest/backup.xml + volid="$volpath" + if [[ ${#volume_uuid_arr[@]} -gt $disk_index && -n "${volume_uuid_arr[$disk_index]}" ]]; then + volid="${volume_uuid_arr[$disk_index]}" + fi + echo "" >> $dest/backup.xml name="datadisk" + ((disk_index+=1)) done echo "" >> $dest/backup.xml @@ -162,10 +174,17 @@ backup_stopped_vm() { mkdir -p "$dest" || { echo "Failed to create backup directory $dest"; exit 1; } IFS="," + local -a volume_uuid_arr=() + if [[ -n "$VOLUME_UUIDS" ]]; then + IFS=',' read -r -a volume_uuid_arr <<< "$VOLUME_UUIDS" + fi name="root" + local disk_index=0 for disk in $DISK_PATHS; do - if [[ "$disk" == rbd:* ]]; then + if [[ ${#volume_uuid_arr[@]} -gt $disk_index && -n "${volume_uuid_arr[$disk_index]}" ]]; then + volUuid="${volume_uuid_arr[$disk_index]}" + elif [[ "$disk" == rbd:* ]]; then # disk for rbd => rbd:/:mon_host=... # sample: rbd:cloudstack/53d5c355-d726-4d3e-9422-046a503a0b12:mon_host=10.0.1.2... beforeUuid="${disk#*/}" # Remove up to first slash after rbd: @@ -179,6 +198,7 @@ backup_stopped_vm() { cleanup fi name="datadisk" + ((disk_index+=1)) done sync @@ -280,6 +300,11 @@ while [[ $# -gt 0 ]]; do shift shift ;; + -u|--volumeuuids) + VOLUME_UUIDS="$2" + shift + shift + ;; -h|--help) usage shift @@ -305,4 +330,4 @@ elif [ "$OP" = "delete" ]; then delete_backup elif [ "$OP" = "stats" ]; then get_backup_stats -fi \ No newline at end of file +fi diff --git a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java index 97aeff2d44c0..1c9c0f6d7871 100644 --- a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java @@ -1702,6 +1702,20 @@ private void syncOutOfBandBackups(final BackupProvider backupProvider, DataCente backupProvider.syncBackupMetrics(dataCenter.getId()); for (final VMInstanceVO vm : vms) { try { + Long backupOfferingId = vm.getBackupOfferingId(); + if (backupOfferingId == null) { + logger.debug("Skipping VM [{}] because backup offering is not assigned.", vm); + continue; + } + BackupOfferingVO offering = backupOfferingDao.findById(vm.getBackupOfferingId()); + if (offering == null) { + logger.debug("Skipping VM [{}] because backup offering [{}] was not found.", vm, backupOfferingId); + continue; + } + if (!backupProvider.getName().equalsIgnoreCase(offering.getProvider())) { + logger.debug("Skipping VM [{}] because backup offering provider [{}] does not match current provider [{}].", vm, offering.getProvider(), backupProvider.getName()); + continue; + } logger.debug(String.format("Trying to sync backups of VM [%s] using backup provider [%s].", vm, backupProvider.getName())); // Sync out-of-band backups syncBackups(backupProvider, vm); diff --git a/ui/src/views/compute/StartBackup.vue b/ui/src/views/compute/StartBackup.vue index 88320506100f..c3bb7f24d153 100644 --- a/ui/src/views/compute/StartBackup.vue +++ b/ui/src/views/compute/StartBackup.vue @@ -125,7 +125,7 @@ export default { api('createBackup', data).then(response => { this.$pollJob({ jobId: response.createbackupresponse.jobid, - title: this.$t('label.create.bucket'), + title: this.$t('label.create.backup'), description: values.name, errorMessage: this.$t('message.create.backup.failed'), loadingMessage: `${this.$t('label.create.backup')}: ${this.resource.name || this.resource.id}`,