Skip to content
Closed
Prev Previous commit
Next Next commit
Fix issue when restoring backup after migration of volume (#12549)
  • Loading branch information
Pearl1594 authored Feb 13, 2026
commit ae5308bdd2051578096ae1a0fb11412ac835d97b
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ public class RestoreBackupCommand extends Command {
private String backupRepoType;
private String backupRepoAddress;
private List<String> volumePaths;
private List<String> backupFiles;
private String diskType;
private Boolean vmExists;
private String restoreVolumeUUID;
private VirtualMachine.State vmState;

protected RestoreBackupCommand() {
Expand Down Expand Up @@ -80,6 +80,14 @@ public void setVolumePaths(List<String> volumePaths) {
this.volumePaths = volumePaths;
}

public List<String> getBackupFiles() {
return backupFiles;
}

public void setBackupFiles(List<String> backupFiles) {
this.backupFiles = backupFiles;
}

public Boolean isVmExists() {
return vmExists;
}
Expand All @@ -104,14 +112,6 @@ public void setMountOptions(String mountOptions) {
this.mountOptions = mountOptions;
}

public String getRestoreVolumeUUID() {
return restoreVolumeUUID;
}

public void setRestoreVolumeUUID(String restoreVolumeUUID) {
this.restoreVolumeUUID = restoreVolumeUUID;
}

public VirtualMachine.State getVmState() {
return vmState;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@
import java.util.Map;
import java.util.HashMap;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -230,6 +229,7 @@ public boolean restoreVMFromBackup(VirtualMachine vm, Backup backup) {
restoreCommand.setMountOptions(backupRepository.getMountOptions());
restoreCommand.setVmName(vm.getName());
restoreCommand.setVolumePaths(getVolumePaths(volumes));
restoreCommand.setBackupFiles(getBackupFiles(backedVolumes));
restoreCommand.setVmExists(vm.getRemoved() == null);
restoreCommand.setVmState(vm.getState());

Expand All @@ -244,6 +244,14 @@ public boolean restoreVMFromBackup(VirtualMachine vm, Backup backup) {
return answer.getResult();
}

private List<String> getBackupFiles(List<Backup.VolumeInfo> backedVolumes) {
List<String> backupFiles = new ArrayList<>();
for (Backup.VolumeInfo backedVolume : backedVolumes) {
backupFiles.add(backedVolume.getPath());
}
return backupFiles;
}

private List<String> getVolumePaths(List<VolumeVO> volumes) {
List<String> volumePaths = new ArrayList<>();
for (VolumeVO volume : volumes) {
Expand Down Expand Up @@ -271,8 +279,11 @@ public Pair<Boolean, String> restoreBackedUpVolume(Backup backup, String volumeU
final StoragePoolHostVO dataStore = storagePoolHostDao.findByUuid(dataStoreUuid);
final HostVO hostVO = hostDao.findByIp(hostIp);

Optional<Backup.VolumeInfo> matchingVolume = getBackedUpVolumeInfo(backupSourceVm.getBackupVolumeList(), volumeUuid);
Long backedUpVolumeSize = matchingVolume.isPresent() ? matchingVolume.get().getSize() : 0L;
Backup.VolumeInfo matchingVolume = getBackedUpVolumeInfo(backup.getBackedUpVolumes(), volumeUuid);
if (matchingVolume == null) {
throw new CloudRuntimeException(String.format("Unable to find volume %s in the list of backed up volumes for backup %s, cannot proceed with restore", volumeUuid, backup));
}
Long backedUpVolumeSize = matchingVolume.getSize();

Comment thread
DaanHoogland marked this conversation as resolved.
Outdated
LOG.debug("Restoring vm volume {} from backup {} on the NAS Backup Provider", volume, backup);
BackupRepository backupRepository = getBackupRepository(backupSourceVm, backup);
Expand Down Expand Up @@ -300,11 +311,11 @@ public Pair<Boolean, String> restoreBackedUpVolume(Backup backup, String volumeU
restoreCommand.setBackupRepoAddress(backupRepository.getAddress());
restoreCommand.setVmName(vmNameAndState.first());
restoreCommand.setVolumePaths(Collections.singletonList(String.format("%s/%s", dataStore.getLocalPath(), volumeUUID)));
restoreCommand.setBackupFiles(Collections.singletonList(matchingVolume.getPath()));
restoreCommand.setDiskType(volume.getVolumeType().name().toLowerCase(Locale.ROOT));
restoreCommand.setMountOptions(backupRepository.getMountOptions());
restoreCommand.setVmExists(null);
restoreCommand.setVmState(vmNameAndState.second());
restoreCommand.setRestoreVolumeUUID(volumeUuid);

BackupAnswer answer = null;
try {
Expand Down Expand Up @@ -339,10 +350,11 @@ private BackupRepository getBackupRepository(VirtualMachine vm, Backup backup) {
return backupRepository;
}

private Optional<Backup.VolumeInfo> getBackedUpVolumeInfo(List<Backup.VolumeInfo> backedUpVolumes, String volumeUuid) {
private Backup.VolumeInfo getBackedUpVolumeInfo(List<Backup.VolumeInfo> backedUpVolumes, String volumeUuid) {
return backedUpVolumes.stream()
.filter(v -> v.getUuid().equals(volumeUuid))
.findFirst();
.findFirst()
.orElse(null);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import org.apache.cloudstack.backup.RestoreBackupCommand;
import org.apache.commons.lang3.RandomStringUtils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
Expand Down Expand Up @@ -59,20 +58,21 @@ public Answer execute(RestoreBackupCommand command, LibvirtComputingResource ser
Boolean vmExists = command.isVmExists();
String diskType = command.getDiskType();
List<String> volumePaths = command.getVolumePaths();
String restoreVolumeUuid = command.getRestoreVolumeUUID();
List<String> backupFiles = command.getBackupFiles();

String newVolumeId = null;
try {
if (Objects.isNull(vmExists)) {
String volumePath = volumePaths.get(0);
String backupFile = backupFiles.get(0);
int lastIndex = volumePath.lastIndexOf("/");
newVolumeId = volumePath.substring(lastIndex + 1);
restoreVolume(backupPath, backupRepoType, backupRepoAddress, volumePath, diskType, restoreVolumeUuid,
restoreVolume(backupPath, backupRepoType, backupRepoAddress, volumePath, diskType, backupFile,
new Pair<>(vmName, command.getVmState()), mountOptions);
} else if (Boolean.TRUE.equals(vmExists)) {
restoreVolumesOfExistingVM(volumePaths, backupPath, backupRepoType, backupRepoAddress, mountOptions);
restoreVolumesOfExistingVM(volumePaths, backupPath, backupFiles, backupRepoType, backupRepoAddress, mountOptions);
} else {
restoreVolumesOfDestroyedVMs(volumePaths, vmName, backupPath, backupRepoType, backupRepoAddress, mountOptions);
restoreVolumesOfDestroyedVMs(volumePaths, vmName, backupPath, backupFiles, backupRepoType, backupRepoAddress, mountOptions);
}
} catch (CloudRuntimeException e) {
String errorMessage = "Failed to restore backup for VM: " + vmName + ".";
Expand All @@ -86,17 +86,18 @@ public Answer execute(RestoreBackupCommand command, LibvirtComputingResource ser
return new BackupAnswer(command, true, newVolumeId);
}

private void restoreVolumesOfExistingVM(List<String> volumePaths, String backupPath,
private void restoreVolumesOfExistingVM(List<String> volumePaths, String backupPath, List<String> backupFiles,
String backupRepoType, String backupRepoAddress, String mountOptions) {
String diskType = "root";
String mountDirectory = mountBackupDirectory(backupRepoAddress, backupRepoType, mountOptions);
try {
for (int idx = 0; idx < volumePaths.size(); idx++) {
String volumePath = volumePaths.get(idx);
Pair<String, String> bkpPathAndVolUuid = getBackupPath(mountDirectory, volumePath, backupPath, diskType, null);
String backupFile = backupFiles.get(idx);
String bkpPath = getBackupPath(mountDirectory, backupPath, backupFile, diskType);
diskType = "datadisk";
if (!replaceVolumeWithBackup(volumePath, bkpPathAndVolUuid.first())) {
throw new CloudRuntimeException(String.format("Unable to restore backup for volume [%s].", bkpPathAndVolUuid.second()));
if (!replaceVolumeWithBackup(volumePath, bkpPath)) {
throw new CloudRuntimeException(String.format("Unable to restore backup from volume [%s].", volumePath));
}
}
} finally {
Expand All @@ -106,17 +107,18 @@ private void restoreVolumesOfExistingVM(List<String> volumePaths, String backupP

}

private void restoreVolumesOfDestroyedVMs(List<String> volumePaths, String vmName, String backupPath,
private void restoreVolumesOfDestroyedVMs(List<String> volumePaths, String vmName, String backupPath, List<String> backupFiles,
String backupRepoType, String backupRepoAddress, String mountOptions) {
String mountDirectory = mountBackupDirectory(backupRepoAddress, backupRepoType, mountOptions);
String diskType = "root";
try {
for (int i = 0; i < volumePaths.size(); i++) {
String volumePath = volumePaths.get(i);
Pair<String, String> bkpPathAndVolUuid = getBackupPath(mountDirectory, volumePath, backupPath, diskType, null);
for (int idx = 0; idx < volumePaths.size(); idx++) {
String volumePath = volumePaths.get(idx);
String backupFile = backupFiles.get(idx);
String bkpPath = getBackupPath(mountDirectory, backupPath, backupFile, diskType);
diskType = "datadisk";
if (!replaceVolumeWithBackup(volumePath, bkpPathAndVolUuid.first())) {
throw new CloudRuntimeException(String.format("Unable to restore backup for volume [%s].", bkpPathAndVolUuid.second()));
if (!replaceVolumeWithBackup(volumePath, bkpPath)) {
throw new CloudRuntimeException(String.format("Unable to restore backup from volume [%s].", volumePath));
}
}
} finally {
Expand All @@ -126,13 +128,13 @@ private void restoreVolumesOfDestroyedVMs(List<String> volumePaths, String vmNam
}

private void restoreVolume(String backupPath, String backupRepoType, String backupRepoAddress, String volumePath,
String diskType, String volumeUUID, Pair<String, VirtualMachine.State> vmNameAndState, String mountOptions) {
String diskType, String backupFile, Pair<String, VirtualMachine.State> vmNameAndState, String mountOptions) {
String mountDirectory = mountBackupDirectory(backupRepoAddress, backupRepoType, mountOptions);
Pair<String, String> bkpPathAndVolUuid;
String bkpPath;
try {
bkpPathAndVolUuid = getBackupPath(mountDirectory, volumePath, backupPath, diskType, volumeUUID);
if (!replaceVolumeWithBackup(volumePath, bkpPathAndVolUuid.first())) {
throw new CloudRuntimeException(String.format("Unable to restore backup for volume [%s].", bkpPathAndVolUuid.second()));
bkpPath = getBackupPath(mountDirectory, backupPath, backupFile, diskType);
if (!replaceVolumeWithBackup(volumePath, bkpPath)) {
throw new CloudRuntimeException(String.format("Unable to restore backup from volume [%s].", volumePath));
}
if (VirtualMachine.State.Running.equals(vmNameAndState.second())) {
if (!attachVolumeToVm(vmNameAndState.first(), volumePath)) {
Expand Down Expand Up @@ -188,13 +190,11 @@ private void deleteTemporaryDirectory(String backupDirectory) {
}
}

private Pair<String, String> getBackupPath(String mountDirectory, String volumePath, String backupPath, String diskType, String volumeUuid) {
private String getBackupPath(String mountDirectory, String backupPath, String backupFile, String diskType) {
String bkpPath = String.format(FILE_PATH_PLACEHOLDER, mountDirectory, backupPath);
int lastIndex = volumePath.lastIndexOf(File.separator);
String volUuid = Objects.isNull(volumeUuid) ? volumePath.substring(lastIndex + 1) : volumeUuid;
String backupFileName = String.format("%s.%s.qcow2", diskType.toLowerCase(Locale.ROOT), volUuid);
String backupFileName = String.format("%s.%s.qcow2", diskType.toLowerCase(Locale.ROOT), backupFile);
bkpPath = String.format(FILE_PATH_PLACEHOLDER, bkpPath, backupFileName);
return new Pair<>(bkpPath, volUuid);
return bkpPath;
}

private boolean replaceVolumeWithBackup(String volumePath, String backupPath) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,7 @@ public boolean restoreBackupVolumeAndAttachToVM(final String backedUpVolumeUuid,
throw new CloudRuntimeException(String.format("Error restoring volume [%s] of VM [%s] to host [%s] using backup provider [%s] due to: [%s].",
backedUpVolumeUuid, vm.getUuid(), host.getUuid(), backupProvider.getName(), result.second()));
}
if (!attachVolumeToVM(vm.getDataCenterId(), result.second(), vmFromBackup.getBackupVolumeList(),
if (!attachVolumeToVM(vm.getDataCenterId(), result.second(), backup.getBackedUpVolumes(),
backedUpVolumeUuid, vm, datastore.getUuid(), backup)) {
throw new CloudRuntimeException(String.format("Error attaching volume [%s] to VM [%s]." + backedUpVolumeUuid, vm.getUuid()));
}
Expand Down
Loading