mirror of https://github.com/apache/cloudstack.git
Merge branch '4.22'
This commit is contained in:
commit
c3614098da
|
|
@ -34,6 +34,7 @@ public class RestoreBackupCommand extends Command {
|
|||
private List<String> backupVolumesUUIDs;
|
||||
private List<PrimaryDataStoreTO> restoreVolumePools;
|
||||
private List<String> restoreVolumePaths;
|
||||
private List<Long> restoreVolumeSizes;
|
||||
private List<String> backupFiles;
|
||||
private String diskType;
|
||||
private Boolean vmExists;
|
||||
|
|
@ -92,6 +93,14 @@ public class RestoreBackupCommand extends Command {
|
|||
this.restoreVolumePaths = restoreVolumePaths;
|
||||
}
|
||||
|
||||
public List<Long> getRestoreVolumeSizes() {
|
||||
return restoreVolumeSizes;
|
||||
}
|
||||
|
||||
public void setRestoreVolumeSizes(List<Long> restoreVolumeSizes) {
|
||||
this.restoreVolumeSizes = restoreVolumeSizes;
|
||||
}
|
||||
|
||||
public List<String> getBackupFiles() {
|
||||
return backupFiles;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,5 +18,3 @@
|
|||
--;
|
||||
-- Schema upgrade cleanup from 4.22.0.0 to 4.22.1.0
|
||||
--;
|
||||
|
||||
DROP VIEW IF EXISTS `cloud`.`account_netstats_view`;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
-- Licensed to the Apache Software Foundation (ASF) under one
|
||||
-- or more contributor license agreements. See the NOTICE file
|
||||
-- distributed with this work for additional information
|
||||
-- regarding copyright ownership. The ASF licenses this file
|
||||
-- to you under the Apache License, Version 2.0 (the
|
||||
-- "License"); you may not use this file except in compliance
|
||||
-- with the License. You may obtain a copy of the License at
|
||||
--
|
||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||
--
|
||||
-- Unless required by applicable law or agreed to in writing,
|
||||
-- software distributed under the License is distributed on an
|
||||
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
-- KIND, either express or implied. See the License for the
|
||||
-- specific language governing permissions and limitations
|
||||
-- under the License.
|
||||
|
||||
-- cloud.account_netstats_view source
|
||||
|
||||
|
||||
DROP VIEW IF EXISTS `cloud`.`account_netstats_view`;
|
||||
|
||||
CREATE VIEW `cloud`.`account_netstats_view` AS
|
||||
select
|
||||
`user_statistics`.`account_id` AS `account_id`,
|
||||
(sum(`user_statistics`.`net_bytes_received`) + sum(`user_statistics`.`current_bytes_received`)) AS `bytesReceived`,
|
||||
(sum(`user_statistics`.`net_bytes_sent`) + sum(`user_statistics`.`current_bytes_sent`)) AS `bytesSent`
|
||||
from
|
||||
`user_statistics`
|
||||
group by
|
||||
`user_statistics`.`account_id`;
|
||||
|
|
@ -39,8 +39,8 @@ select
|
|||
`data_center`.`id` AS `data_center_id`,
|
||||
`data_center`.`uuid` AS `data_center_uuid`,
|
||||
`data_center`.`name` AS `data_center_name`,
|
||||
`account_netstats`.`bytesReceived` AS `bytesReceived`,
|
||||
`account_netstats`.`bytesSent` AS `bytesSent`,
|
||||
`account_netstats_view`.`bytesReceived` AS `bytesReceived`,
|
||||
`account_netstats_view`.`bytesSent` AS `bytesSent`,
|
||||
`vmlimit`.`max` AS `vmLimit`,
|
||||
`vmcount`.`count` AS `vmTotal`,
|
||||
`runningvm`.`vmcount` AS `runningVms`,
|
||||
|
|
@ -89,15 +89,8 @@ from
|
|||
`cloud`.`domain` ON account.domain_id = domain.id
|
||||
left join
|
||||
`cloud`.`data_center` ON account.default_zone_id = data_center.id
|
||||
left join lateral (
|
||||
select
|
||||
coalesce(sum(`user_statistics`.`net_bytes_received` + `user_statistics`.`current_bytes_received`), 0) AS `bytesReceived`,
|
||||
coalesce(sum(`user_statistics`.`net_bytes_sent` + `user_statistics`.`current_bytes_sent`), 0) AS `bytesSent`
|
||||
from
|
||||
`cloud`.`user_statistics`
|
||||
where
|
||||
`user_statistics`.`account_id` = `account`.`id`
|
||||
) AS `account_netstats` ON TRUE
|
||||
left join
|
||||
`cloud`.`account_netstats_view` ON account.id = account_netstats_view.account_id
|
||||
left join
|
||||
`cloud`.`resource_limit` vmlimit ON account.id = vmlimit.account_id
|
||||
and vmlimit.type = 'user_vm' and vmlimit.tag IS NULL
|
||||
|
|
|
|||
|
|
@ -357,7 +357,8 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co
|
|||
volumePools.add(dataStore != null ? (PrimaryDataStoreTO)dataStore.getTO() : null);
|
||||
|
||||
String volumePathPrefix = getVolumePathPrefix(storagePool);
|
||||
volumePaths.add(String.format("%s/%s", volumePathPrefix, volume.getPath()));
|
||||
String volumePathSuffix = getVolumePathSuffix(storagePool);
|
||||
volumePaths.add(String.format("%s%s%s", volumePathPrefix, volume.getPath(), volumePathSuffix));
|
||||
}
|
||||
return new Pair<>(volumePools, volumePaths);
|
||||
}
|
||||
|
|
@ -367,14 +368,24 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co
|
|||
if (ScopeType.HOST.equals(storagePool.getScope()) ||
|
||||
Storage.StoragePoolType.SharedMountPoint.equals(storagePool.getPoolType()) ||
|
||||
Storage.StoragePoolType.RBD.equals(storagePool.getPoolType())) {
|
||||
volumePathPrefix = storagePool.getPath();
|
||||
volumePathPrefix = storagePool.getPath() + "/";
|
||||
} else if (Storage.StoragePoolType.Linstor.equals(storagePool.getPoolType())) {
|
||||
volumePathPrefix = "/dev/drbd/by-res/cs-";
|
||||
} else {
|
||||
// Should be Storage.StoragePoolType.NetworkFilesystem
|
||||
volumePathPrefix = String.format("/mnt/%s", storagePool.getUuid());
|
||||
volumePathPrefix = String.format("/mnt/%s/", storagePool.getUuid());
|
||||
}
|
||||
return volumePathPrefix;
|
||||
}
|
||||
|
||||
private String getVolumePathSuffix(StoragePoolVO storagePool) {
|
||||
if (Storage.StoragePoolType.Linstor.equals(storagePool.getPoolType())) {
|
||||
return "/0";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<Boolean, String> restoreBackedUpVolume(Backup backup, Backup.VolumeInfo backupVolumeInfo, String hostIp, String dataStoreUuid, Pair<String, VirtualMachine.State> vmNameAndState) {
|
||||
final VolumeVO volume = volumeDao.findByUuid(backupVolumeInfo.getUuid());
|
||||
|
|
@ -419,7 +430,9 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co
|
|||
restoreCommand.setBackupRepoType(backupRepository.getType());
|
||||
restoreCommand.setBackupRepoAddress(backupRepository.getAddress());
|
||||
restoreCommand.setVmName(vmNameAndState.first());
|
||||
restoreCommand.setRestoreVolumePaths(Collections.singletonList(String.format("%s/%s", getVolumePathPrefix(pool), volumeUUID)));
|
||||
String restoreVolumePath = String.format("%s%s%s", getVolumePathPrefix(pool), volumeUUID, getVolumePathSuffix(pool));
|
||||
restoreCommand.setRestoreVolumePaths(Collections.singletonList(restoreVolumePath));
|
||||
restoreCommand.setRestoreVolumeSizes(Collections.singletonList(backedUpVolumeSize));
|
||||
DataStore dataStore = dataStoreMgr.getDataStore(pool.getId(), DataStoreRole.Primary);
|
||||
restoreCommand.setRestoreVolumePools(Collections.singletonList(dataStore != null ? (PrimaryDataStoreTO)dataStore.getTO() : null));
|
||||
restoreCommand.setDiskType(backupVolumeInfo.getType().name().toLowerCase(Locale.ROOT));
|
||||
|
|
|
|||
|
|
@ -41,9 +41,9 @@ import org.apache.commons.lang3.RandomStringUtils;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.libvirt.LibvirtException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
|
@ -56,10 +56,25 @@ public class LibvirtRestoreBackupCommandWrapper extends CommandWrapper<RestoreBa
|
|||
private static final String UMOUNT_COMMAND = "sudo umount %s";
|
||||
private static final String FILE_PATH_PLACEHOLDER = "%s/%s";
|
||||
private static final String ATTACH_QCOW2_DISK_COMMAND = " virsh attach-disk %s %s %s --driver qemu --subdriver qcow2 --cache none";
|
||||
private static final String ATTACH_RAW_DISK_COMMAND = " virsh attach-disk %s %s %s --driver qemu --cache none";
|
||||
private static final String ATTACH_RBD_DISK_XML_COMMAND = " virsh attach-device %s /dev/stdin <<EOF%sEOF";
|
||||
private static final String CURRRENT_DEVICE = "virsh domblklist --domain %s | tail -n 3 | head -n 1 | awk '{print $1}'";
|
||||
private static final String RSYNC_COMMAND = "rsync -az %s %s";
|
||||
|
||||
private String getVolumeUuidFromPath(String volumePath, PrimaryDataStoreTO volumePool) {
|
||||
if (Storage.StoragePoolType.Linstor.equals(volumePool.getPoolType())) {
|
||||
Path path = Paths.get(volumePath);
|
||||
String rscName = path.getParent().getFileName().toString();
|
||||
if (rscName.startsWith("cs-")) {
|
||||
rscName = rscName.substring(3);
|
||||
}
|
||||
return rscName;
|
||||
} else {
|
||||
int lastIndex = volumePath.lastIndexOf("/");
|
||||
return volumePath.substring(lastIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Answer execute(RestoreBackupCommand command, LibvirtComputingResource serverResource) {
|
||||
String vmName = command.getVmName();
|
||||
|
|
@ -84,9 +99,9 @@ public class LibvirtRestoreBackupCommandWrapper extends CommandWrapper<RestoreBa
|
|||
PrimaryDataStoreTO volumePool = restoreVolumePools.get(0);
|
||||
String volumePath = restoreVolumePaths.get(0);
|
||||
String backupFile = backupFiles.get(0);
|
||||
int lastIndex = volumePath.lastIndexOf("/");
|
||||
newVolumeId = volumePath.substring(lastIndex + 1);
|
||||
restoreVolume(storagePoolMgr, backupPath, volumePool, volumePath, diskType, backupFile,
|
||||
newVolumeId = getVolumeUuidFromPath(volumePath, volumePool);
|
||||
Long size = command.getRestoreVolumeSizes().get(0);
|
||||
restoreVolume(storagePoolMgr, backupPath, volumePool, volumePath, diskType, backupFile, size,
|
||||
new Pair<>(vmName, command.getVmState()), mountDirectory, timeout);
|
||||
} else if (Boolean.TRUE.equals(vmExists)) {
|
||||
restoreVolumesOfExistingVM(storagePoolMgr, restoreVolumePools, restoreVolumePaths, backedVolumeUUIDs, backupPath, backupFiles, mountDirectory, timeout);
|
||||
|
|
@ -143,7 +158,7 @@ public class LibvirtRestoreBackupCommandWrapper extends CommandWrapper<RestoreBa
|
|||
String volumePath = volumePaths.get(i);
|
||||
String backupFile = backupFiles.get(i);
|
||||
String bkpPath = getBackupPath(mountDirectory, backupPath, backupFile, diskType);
|
||||
String volumeUuid = volumePath.substring(volumePath.lastIndexOf(File.separator) + 1);
|
||||
String volumeUuid = getVolumeUuidFromPath(volumePath, volumePool);
|
||||
diskType = "datadisk";
|
||||
verifyBackupFile(bkpPath, volumeUuid);
|
||||
if (!replaceVolumeWithBackup(storagePoolMgr, volumePool, volumePath, bkpPath, timeout)) {
|
||||
|
|
@ -157,14 +172,14 @@ public class LibvirtRestoreBackupCommandWrapper extends CommandWrapper<RestoreBa
|
|||
}
|
||||
|
||||
private void restoreVolume(KVMStoragePoolManager storagePoolMgr, String backupPath, PrimaryDataStoreTO volumePool, String volumePath, String diskType, String backupFile,
|
||||
Pair<String, VirtualMachine.State> vmNameAndState, String mountDirectory, int timeout) {
|
||||
Long size, Pair<String, VirtualMachine.State> vmNameAndState, String mountDirectory, int timeout) {
|
||||
String bkpPath;
|
||||
String volumeUuid;
|
||||
try {
|
||||
bkpPath = getBackupPath(mountDirectory, backupPath, backupFile, diskType);
|
||||
volumeUuid = volumePath.substring(volumePath.lastIndexOf(File.separator) + 1);
|
||||
volumeUuid = getVolumeUuidFromPath(volumePath, volumePool);
|
||||
verifyBackupFile(bkpPath, volumeUuid);
|
||||
if (!replaceVolumeWithBackup(storagePoolMgr, volumePool, volumePath, bkpPath, timeout, true)) {
|
||||
if (!replaceVolumeWithBackup(storagePoolMgr, volumePool, volumePath, bkpPath, timeout, true, size)) {
|
||||
throw new CloudRuntimeException(String.format("Unable to restore contents from the backup volume [%s].", volumeUuid));
|
||||
|
||||
}
|
||||
|
|
@ -247,42 +262,66 @@ public class LibvirtRestoreBackupCommandWrapper extends CommandWrapper<RestoreBa
|
|||
}
|
||||
|
||||
private boolean replaceVolumeWithBackup(KVMStoragePoolManager storagePoolMgr, PrimaryDataStoreTO volumePool, String volumePath, String backupPath, int timeout) {
|
||||
return replaceVolumeWithBackup(storagePoolMgr, volumePool, volumePath, backupPath, timeout, false);
|
||||
return replaceVolumeWithBackup(storagePoolMgr, volumePool, volumePath, backupPath, timeout, false, null);
|
||||
}
|
||||
|
||||
private boolean replaceVolumeWithBackup(KVMStoragePoolManager storagePoolMgr, PrimaryDataStoreTO volumePool, String volumePath, String backupPath, int timeout, boolean createTargetVolume) {
|
||||
if (volumePool.getPoolType() != Storage.StoragePoolType.RBD) {
|
||||
int exitValue = Script.runSimpleBashScriptForExitValue(String.format(RSYNC_COMMAND, backupPath, volumePath));
|
||||
return exitValue == 0;
|
||||
private boolean replaceVolumeWithBackup(KVMStoragePoolManager storagePoolMgr, PrimaryDataStoreTO volumePool, String volumePath, String backupPath, int timeout, boolean createTargetVolume, Long size) {
|
||||
if (List.of(Storage.StoragePoolType.RBD, Storage.StoragePoolType.Linstor).contains(volumePool.getPoolType())) {
|
||||
return replaceBlockDeviceWithBackup(storagePoolMgr, volumePool, volumePath, backupPath, timeout, createTargetVolume, size);
|
||||
}
|
||||
|
||||
return replaceRbdVolumeWithBackup(storagePoolMgr, volumePool, volumePath, backupPath, timeout, createTargetVolume);
|
||||
int exitValue = Script.runSimpleBashScriptForExitValue(String.format(RSYNC_COMMAND, backupPath, volumePath));
|
||||
return exitValue == 0;
|
||||
}
|
||||
|
||||
private boolean replaceRbdVolumeWithBackup(KVMStoragePoolManager storagePoolMgr, PrimaryDataStoreTO volumePool, String volumePath, String backupPath, int timeout, boolean createTargetVolume) {
|
||||
private boolean replaceBlockDeviceWithBackup(KVMStoragePoolManager storagePoolMgr, PrimaryDataStoreTO volumePool, String volumePath, String backupPath, int timeout, boolean createTargetVolume, Long size) {
|
||||
KVMStoragePool volumeStoragePool = storagePoolMgr.getStoragePool(volumePool.getPoolType(), volumePool.getUuid());
|
||||
QemuImg qemu;
|
||||
try {
|
||||
qemu = new QemuImg(timeout * 1000, true, false);
|
||||
if (!createTargetVolume) {
|
||||
KVMPhysicalDisk rdbDisk = volumeStoragePool.getPhysicalDisk(volumePath);
|
||||
logger.debug("Restoring RBD volume: {}", rdbDisk.toString());
|
||||
String volumeUuid = getVolumeUuidFromPath(volumePath, volumePool);
|
||||
KVMPhysicalDisk disk = null;
|
||||
if (createTargetVolume) {
|
||||
if (Storage.StoragePoolType.Linstor.equals(volumePool.getPoolType())) {
|
||||
if (size == null) {
|
||||
throw new CloudRuntimeException("Restore volume size is required for Linstor pool when creating target volume");
|
||||
}
|
||||
disk = volumeStoragePool.createPhysicalDisk(volumeUuid, QemuImg.PhysicalDiskFormat.RAW, Storage.ProvisioningType.THIN, size, null);
|
||||
}
|
||||
} else {
|
||||
if (Storage.StoragePoolType.Linstor.equals(volumePool.getPoolType())) {
|
||||
storagePoolMgr.connectPhysicalDisk(volumePool.getPoolType(), volumePool.getUuid(), volumeUuid, null);
|
||||
} else {
|
||||
disk = volumeStoragePool.getPhysicalDisk(volumePath);
|
||||
}
|
||||
qemu.setSkipTargetVolumeCreation(true);
|
||||
}
|
||||
if (disk != null) {
|
||||
logger.debug("Restoring volume: {}", disk.toString());
|
||||
}
|
||||
} catch (LibvirtException ex) {
|
||||
throw new CloudRuntimeException("Failed to create qemu-img command to restore RBD volume with backup", ex);
|
||||
throw new CloudRuntimeException(String.format("Failed to create qemu-img command to restore %s volume with backup", volumePool.getPoolType()), ex);
|
||||
}
|
||||
|
||||
QemuImgFile srcBackupFile = null;
|
||||
QemuImgFile destVolumeFile = null;
|
||||
try {
|
||||
srcBackupFile = new QemuImgFile(backupPath, QemuImg.PhysicalDiskFormat.QCOW2);
|
||||
String rbdDestVolumeFile = KVMPhysicalDisk.RBDStringBuilder(volumeStoragePool, volumePath);
|
||||
destVolumeFile = new QemuImgFile(rbdDestVolumeFile, QemuImg.PhysicalDiskFormat.RAW);
|
||||
|
||||
logger.debug("Starting convert backup {} to RBD volume {}", backupPath, volumePath);
|
||||
String destVolume;
|
||||
switch(volumePool.getPoolType()) {
|
||||
case Linstor:
|
||||
destVolume = volumePath;
|
||||
break;
|
||||
case RBD:
|
||||
destVolume = KVMPhysicalDisk.RBDStringBuilder(volumeStoragePool, volumePath);
|
||||
break;
|
||||
default:
|
||||
throw new CloudRuntimeException(String.format("Unsupported storage pool type [%s] for block device restore with backup.", volumePool.getPoolType()));
|
||||
}
|
||||
destVolumeFile = new QemuImgFile(destVolume, QemuImg.PhysicalDiskFormat.RAW);
|
||||
logger.debug("Starting convert backup {} to volume {}", backupPath, volumePath);
|
||||
qemu.convert(srcBackupFile, destVolumeFile);
|
||||
logger.debug("Successfully converted backup {} to RBD volume {}", backupPath, volumePath);
|
||||
logger.debug("Successfully converted backup {} to volume {}", backupPath, volumePath);
|
||||
} catch (QemuImgException | LibvirtException e) {
|
||||
String srcFilename = srcBackupFile != null ? srcBackupFile.getFileName() : null;
|
||||
String destFilename = destVolumeFile != null ? destVolumeFile.getFileName() : null;
|
||||
|
|
@ -296,12 +335,14 @@ public class LibvirtRestoreBackupCommandWrapper extends CommandWrapper<RestoreBa
|
|||
private boolean attachVolumeToVm(KVMStoragePoolManager storagePoolMgr, String vmName, PrimaryDataStoreTO volumePool, String volumePath) {
|
||||
String deviceToAttachDiskTo = getDeviceToAttachDisk(vmName);
|
||||
int exitValue;
|
||||
if (volumePool.getPoolType() != Storage.StoragePoolType.RBD) {
|
||||
exitValue = Script.runSimpleBashScriptForExitValue(String.format(ATTACH_QCOW2_DISK_COMMAND, vmName, volumePath, deviceToAttachDiskTo));
|
||||
} else {
|
||||
if (volumePool.getPoolType() == Storage.StoragePoolType.RBD) {
|
||||
String xmlForRbdDisk = getXmlForRbdDisk(storagePoolMgr, volumePool, volumePath, deviceToAttachDiskTo);
|
||||
logger.debug("RBD disk xml to attach: {}", xmlForRbdDisk);
|
||||
exitValue = Script.runSimpleBashScriptForExitValue(String.format(ATTACH_RBD_DISK_XML_COMMAND, vmName, xmlForRbdDisk));
|
||||
} else if (volumePool.getPoolType() == Storage.StoragePoolType.Linstor) {
|
||||
exitValue = Script.runSimpleBashScriptForExitValue(String.format(ATTACH_RAW_DISK_COMMAND, vmName, volumePath, deviceToAttachDiskTo));
|
||||
} else {
|
||||
exitValue = Script.runSimpleBashScriptForExitValue(String.format(ATTACH_QCOW2_DISK_COMMAND, vmName, volumePath, deviceToAttachDiskTo));
|
||||
}
|
||||
return exitValue == 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package com.cloud.hypervisor.kvm.resource.wrapper;
|
|||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.utils.script.Script;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import org.apache.cloudstack.backup.BackupAnswer;
|
||||
|
|
@ -66,7 +67,10 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
when(command.getMountOptions()).thenReturn("rw");
|
||||
when(command.isVmExists()).thenReturn(null);
|
||||
when(command.getDiskType()).thenReturn("root");
|
||||
when(command.getRestoreVolumeSizes()).thenReturn(Arrays.asList(1024L));
|
||||
when(command.getWait()).thenReturn(60);
|
||||
PrimaryDataStoreTO primaryDataStore = Mockito.mock(PrimaryDataStoreTO.class);
|
||||
when(primaryDataStore.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(command.getRestoreVolumePools()).thenReturn(Arrays.asList(primaryDataStore));
|
||||
when(command.getRestoreVolumePaths()).thenReturn(Arrays.asList("/var/lib/libvirt/images/volume-123"));
|
||||
when(command.getBackupFiles()).thenReturn(Arrays.asList("volume-123"));
|
||||
|
|
@ -109,6 +113,7 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
when(command.isVmExists()).thenReturn(true);
|
||||
when(command.getDiskType()).thenReturn("root");
|
||||
PrimaryDataStoreTO primaryDataStore = Mockito.mock(PrimaryDataStoreTO.class);
|
||||
when(primaryDataStore.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(command.getRestoreVolumePools()).thenReturn(Arrays.asList(primaryDataStore));
|
||||
when(command.getRestoreVolumePaths()).thenReturn(Arrays.asList("/var/lib/libvirt/images/volume-123"));
|
||||
when(command.getBackupVolumesUUIDs()).thenReturn(Arrays.asList("volume-123"));
|
||||
|
|
@ -148,6 +153,7 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
when(command.isVmExists()).thenReturn(false);
|
||||
when(command.getDiskType()).thenReturn("root");
|
||||
PrimaryDataStoreTO primaryDataStore = Mockito.mock(PrimaryDataStoreTO.class);
|
||||
when(primaryDataStore.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(command.getRestoreVolumePools()).thenReturn(Arrays.asList(primaryDataStore));
|
||||
when(command.getRestoreVolumePaths()).thenReturn(Arrays.asList("/var/lib/libvirt/images/volume-123"));
|
||||
when(command.getBackupFiles()).thenReturn(Arrays.asList("volume-123"));
|
||||
|
|
@ -185,7 +191,10 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
when(command.getMountOptions()).thenReturn("username=user,password=pass");
|
||||
when(command.isVmExists()).thenReturn(null);
|
||||
when(command.getDiskType()).thenReturn("root");
|
||||
when(command.getRestoreVolumeSizes()).thenReturn(Arrays.asList(1024L));
|
||||
when(command.getWait()).thenReturn(60);
|
||||
PrimaryDataStoreTO primaryDataStore = Mockito.mock(PrimaryDataStoreTO.class);
|
||||
when(primaryDataStore.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(command.getRestoreVolumePools()).thenReturn(Arrays.asList(primaryDataStore));
|
||||
when(command.getRestoreVolumePaths()).thenReturn(Arrays.asList("/var/lib/libvirt/images/volume-123"));
|
||||
when(command.getBackupFiles()).thenReturn(Arrays.asList("volume-123"));
|
||||
|
|
@ -226,7 +235,10 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
lenient().when(command.getMountOptions()).thenReturn("rw");
|
||||
lenient().when(command.isVmExists()).thenReturn(null);
|
||||
lenient().when(command.getDiskType()).thenReturn("root");
|
||||
lenient().when(command.getRestoreVolumeSizes()).thenReturn(Arrays.asList(1024L));
|
||||
lenient().when(command.getWait()).thenReturn(60);
|
||||
PrimaryDataStoreTO primaryDataStore = Mockito.mock(PrimaryDataStoreTO.class);
|
||||
lenient().when(primaryDataStore.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(command.getRestoreVolumePools()).thenReturn(Arrays.asList(primaryDataStore));
|
||||
lenient().when(command.getRestoreVolumePaths()).thenReturn(Arrays.asList("/var/lib/libvirt/images/volume-123"));
|
||||
when(command.getBackupFiles()).thenReturn(Arrays.asList("volume-123"));
|
||||
|
|
@ -262,7 +274,10 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
when(command.getMountOptions()).thenReturn("rw");
|
||||
when(command.isVmExists()).thenReturn(null);
|
||||
when(command.getDiskType()).thenReturn("root");
|
||||
when(command.getRestoreVolumeSizes()).thenReturn(Arrays.asList(1024L));
|
||||
when(command.getWait()).thenReturn(60);
|
||||
PrimaryDataStoreTO primaryDataStore = Mockito.mock(PrimaryDataStoreTO.class);
|
||||
when(primaryDataStore.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(command.getRestoreVolumePools()).thenReturn(Arrays.asList(primaryDataStore));
|
||||
when(command.getRestoreVolumePaths()).thenReturn(Arrays.asList("/var/lib/libvirt/images/volume-123"));
|
||||
when(command.getBackupFiles()).thenReturn(Arrays.asList("volume-123"));
|
||||
|
|
@ -308,7 +323,10 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
when(command.getMountOptions()).thenReturn("rw");
|
||||
when(command.isVmExists()).thenReturn(null);
|
||||
when(command.getDiskType()).thenReturn("root");
|
||||
when(command.getRestoreVolumeSizes()).thenReturn(Arrays.asList(1024L));
|
||||
when(command.getWait()).thenReturn(60);
|
||||
PrimaryDataStoreTO primaryDataStore = Mockito.mock(PrimaryDataStoreTO.class);
|
||||
when(primaryDataStore.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(command.getRestoreVolumePools()).thenReturn(Arrays.asList(primaryDataStore));
|
||||
when(command.getRestoreVolumePaths()).thenReturn(Arrays.asList("/var/lib/libvirt/images/volume-123"));
|
||||
when(command.getBackupFiles()).thenReturn(Arrays.asList("volume-123"));
|
||||
|
|
@ -356,7 +374,10 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
when(command.getMountOptions()).thenReturn("rw");
|
||||
when(command.isVmExists()).thenReturn(null);
|
||||
when(command.getDiskType()).thenReturn("root");
|
||||
when(command.getRestoreVolumeSizes()).thenReturn(Arrays.asList(1024L));
|
||||
when(command.getWait()).thenReturn(60);
|
||||
PrimaryDataStoreTO primaryDataStore = Mockito.mock(PrimaryDataStoreTO.class);
|
||||
when(primaryDataStore.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(command.getRestoreVolumePools()).thenReturn(Arrays.asList(primaryDataStore));
|
||||
when(command.getRestoreVolumePaths()).thenReturn(Arrays.asList("/var/lib/libvirt/images/volume-123"));
|
||||
when(command.getBackupFiles()).thenReturn(Arrays.asList("volume-123"));
|
||||
|
|
@ -406,7 +427,10 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
when(command.getMountOptions()).thenReturn("rw");
|
||||
when(command.isVmExists()).thenReturn(null);
|
||||
when(command.getDiskType()).thenReturn("root");
|
||||
when(command.getRestoreVolumeSizes()).thenReturn(Arrays.asList(1024L));
|
||||
when(command.getWait()).thenReturn(60);
|
||||
PrimaryDataStoreTO primaryDataStore = Mockito.mock(PrimaryDataStoreTO.class);
|
||||
when(primaryDataStore.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(command.getRestoreVolumePools()).thenReturn(Arrays.asList(primaryDataStore));
|
||||
when(command.getRestoreVolumePaths()).thenReturn(Arrays.asList("/var/lib/libvirt/images/volume-123"));
|
||||
when(command.getBackupFiles()).thenReturn(Arrays.asList("volume-123"));
|
||||
|
|
@ -460,7 +484,10 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
lenient().when(command.getMountOptions()).thenReturn("rw");
|
||||
lenient().when(command.isVmExists()).thenReturn(null);
|
||||
lenient().when(command.getDiskType()).thenReturn("root");
|
||||
lenient().when(command.getRestoreVolumeSizes()).thenReturn(Arrays.asList(1024L));
|
||||
lenient().when(command.getWait()).thenReturn(60);
|
||||
PrimaryDataStoreTO primaryDataStore = Mockito.mock(PrimaryDataStoreTO.class);
|
||||
lenient().when(primaryDataStore.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(command.getRestoreVolumePools()).thenReturn(Arrays.asList(primaryDataStore));
|
||||
lenient().when(command.getRestoreVolumePaths()).thenReturn(Arrays.asList("/var/lib/libvirt/images/volume-123"));
|
||||
when(command.getBackupFiles()).thenReturn(Arrays.asList("volume-123"));
|
||||
|
|
@ -492,6 +519,8 @@ public class LibvirtRestoreBackupCommandWrapperTest {
|
|||
when(command.getDiskType()).thenReturn("root");
|
||||
PrimaryDataStoreTO primaryDataStore1 = Mockito.mock(PrimaryDataStoreTO.class);
|
||||
PrimaryDataStoreTO primaryDataStore2 = Mockito.mock(PrimaryDataStoreTO.class);
|
||||
when(primaryDataStore1.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(primaryDataStore2.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
when(command.getRestoreVolumePools()).thenReturn(Arrays.asList(
|
||||
primaryDataStore1,
|
||||
primaryDataStore2
|
||||
|
|
|
|||
|
|
@ -90,17 +90,42 @@ sanity_checks() {
|
|||
|
||||
### Operation methods ###
|
||||
|
||||
get_ceph_uuid_from_path() {
|
||||
local fullpath="$1"
|
||||
# disk for rbd => rbd:<pool>/<uuid>:mon_host=<monitor_host>...
|
||||
# sample: rbd:cloudstack/53d5c355-d726-4d3e-9422-046a503a0b12:mon_host=10.0.1.2...
|
||||
local beforeUuid="${fullpath#*/}" # Remove up to first slash after rbd:
|
||||
local volUuid="${beforeUuid%%:*}" # Remove everything after colon to get the uuid
|
||||
echo ""$volUuid""
|
||||
}
|
||||
|
||||
get_linstor_uuid_from_path() {
|
||||
local fullpath="$1"
|
||||
# disk for linstor => /dev/drbd/by-res/cs-<uuid>/0
|
||||
# sample: /dev/drbd/by-res/cs-53d5c355-d726-4d3e-9422-046a503a0b12/0
|
||||
local beforeUuid="${fullpath#/dev/drbd/by-res/}"
|
||||
local volUuid="${beforeUuid%%/*}"
|
||||
volUuid="${volUuid#cs-}"
|
||||
echo "$volUuid"
|
||||
}
|
||||
|
||||
backup_running_vm() {
|
||||
mount_operation
|
||||
mkdir -p "$dest" || { echo "Failed to create backup directory $dest"; exit 1; }
|
||||
|
||||
name="root"
|
||||
echo "<domainbackup mode='push'><disks>" > $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 "<disk name='$disk' backup='yes' type='file' backupmode='full'><driver type='qcow2'/><target file='$dest/$name.$volpath.qcow2' /></disk>" >> $dest/backup.xml
|
||||
while read -r disk fullpath; do
|
||||
if [[ "$fullpath" == /dev/drbd/by-res/* ]]; then
|
||||
volUuid=$(get_linstor_uuid_from_path "$fullpath")
|
||||
else
|
||||
volUuid="${fullpath##*/}"
|
||||
fi
|
||||
echo "<disk name='$disk' backup='yes' type='file' backupmode='full'><driver type='qcow2'/><target file='$dest/$name.$volUuid.qcow2' /></disk>" >> $dest/backup.xml
|
||||
name="datadisk"
|
||||
done
|
||||
done < <(
|
||||
virsh -c qemu:///system domblklist "$VM" --details 2>/dev/null | awk '$2=="disk"{print $3, $4}'
|
||||
)
|
||||
echo "</disks></domainbackup>" >> $dest/backup.xml
|
||||
|
||||
local thaw=0
|
||||
|
|
@ -147,6 +172,25 @@ backup_running_vm() {
|
|||
sleep 5
|
||||
done
|
||||
|
||||
# Use qemu-img convert to sparsify linstor backups which get bloated due to virsh backup-begin.
|
||||
name="root"
|
||||
while read -r disk fullpath; do
|
||||
if [[ "$fullpath" != /dev/drbd/by-res/* ]]; then
|
||||
continue
|
||||
fi
|
||||
volUuid=$(get_linstor_uuid_from_path "$fullpath")
|
||||
if ! qemu-img convert -O qcow2 "$dest/$name.$volUuid.qcow2" "$dest/$name.$volUuid.qcow2.tmp" >> "$logFile" 2> >(cat >&2); then
|
||||
echo "qemu-img convert failed for $dest/$name.$volUuid.qcow2"
|
||||
cleanup
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mv "$dest/$name.$volUuid.qcow2.tmp" "$dest/$name.$volUuid.qcow2"
|
||||
name="datadisk"
|
||||
done < <(
|
||||
virsh -c qemu:///system domblklist "$VM" --details 2>/dev/null | awk '$2=="disk"{print $3, $4}'
|
||||
)
|
||||
|
||||
rm -f $dest/backup.xml
|
||||
sync
|
||||
|
||||
|
|
@ -167,10 +211,9 @@ backup_stopped_vm() {
|
|||
name="root"
|
||||
for disk in $DISK_PATHS; do
|
||||
if [[ "$disk" == rbd:* ]]; then
|
||||
# disk for rbd => rbd:<pool>/<uuid>:mon_host=<monitor_host>...
|
||||
# sample: rbd:cloudstack/53d5c355-d726-4d3e-9422-046a503a0b12:mon_host=10.0.1.2...
|
||||
beforeUuid="${disk#*/}" # Remove up to first slash after rbd:
|
||||
volUuid="${beforeUuid%%:*}" # Remove everything after colon to get the uuid
|
||||
volUuid=$(get_ceph_uuid_from_path "$disk")
|
||||
elif [[ "$disk" == /dev/drbd/by-res/* ]]; then
|
||||
volUuid=$(get_linstor_uuid_from_path "$disk")
|
||||
else
|
||||
volUuid="${disk##*/}"
|
||||
fi
|
||||
|
|
|
|||
Loading…
Reference in New Issue