-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Support Linstor Primary Storage for NAS BnR #12796
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 4.22
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,9 +41,9 @@ | |
| 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 Answer execute(RestoreBackupCommand command, LibvirtComputingResource ser | |
| 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 @@ private void restoreVolumesOfDestroyedVMs(KVMStoragePoolManager storagePoolMgr, | |
| 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 @@ private void restoreVolumesOfDestroyedVMs(KVMStoragePoolManager storagePoolMgr, | |
| } | ||
|
|
||
| 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,28 +262,38 @@ private boolean checkBackupPathExists(String backupPath) { | |
| } | ||
|
|
||
| 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())) { | ||
| 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(getVolumeUuidFromPath(volumePath, volumePool)); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @sureshanaparti can you please review from RBD's regression perspective?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @abh1sar usually, better to lookup by volume path, not the uuid. I checked in my earlier testing, the uuid and path are the same for volumes on ceph, we've to check after migrate volume, any other operation which can changes the path and not uuid. to avoid any issue, can you check for StoragePoolType.RBD and get disk by volume path? |
||
| } | ||
| qemu.setSkipTargetVolumeCreation(true); | ||
| } | ||
| 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); | ||
| } | ||
|
|
@@ -277,12 +302,21 @@ private boolean replaceRbdVolumeWithBackup(KVMStoragePoolManager storagePoolMgr, | |
| 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 +330,14 @@ private boolean replaceRbdVolumeWithBackup(KVMStoragePoolManager storagePoolMgr, | |
| 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; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can keep this path in LinstorUtil, and use it here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't access LinstorUtil from here.