mirror of https://github.com/apache/cloudstack.git
Add support for clvm_ng - which allows qcow2 on block storage , linked clones, etc
This commit is contained in:
parent
81bb667267
commit
cc924c5b3a
|
|
@ -3682,7 +3682,6 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||
disk.defNetworkBasedDisk(glusterVolume + path.replace(mountpoint, ""), pool.getSourceHost(), pool.getSourcePort(), null,
|
||||
null, devId, diskBusType, DiskProtocol.GLUSTER, DiskDef.DiskFmtType.QCOW2);
|
||||
} else if (pool.getType() == StoragePoolType.CLVM || pool.getType() == StoragePoolType.CLVM_NG || physicalDisk.getFormat() == PhysicalDiskFormat.RAW) {
|
||||
// CLVM and CLVM_NG use block devices (/dev/vgname/volume)
|
||||
if (volume.getType() == Volume.Type.DATADISK && !(isWindowsTemplate && isUefiEnabled)) {
|
||||
disk.defBlockBasedDisk(physicalDisk.getPath(), devId, diskBusTypeData);
|
||||
} else {
|
||||
|
|
@ -6585,7 +6584,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||
private static void modifyClvmVolumeState(String volumePath, String lvchangeFlag,
|
||||
String stateDescription, String logMessage) {
|
||||
try {
|
||||
LOGGER.info("[CLVM Migration] {} for volume [{}]", logMessage, volumePath);
|
||||
LOGGER.info("{} for volume [{}]", logMessage, volumePath);
|
||||
|
||||
Script cmd = new Script("lvchange", Duration.standardSeconds(300), LOGGER);
|
||||
cmd.add(lvchangeFlag);
|
||||
|
|
@ -6594,19 +6593,19 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||
String result = cmd.execute();
|
||||
if (result != null) {
|
||||
String errorMsg = String.format(
|
||||
"[CLVM Migration] Failed to set volume [%s] to %s state. Command result: %s",
|
||||
"Failed to set volume [%s] to %s state. Command result: %s",
|
||||
volumePath, stateDescription, result);
|
||||
LOGGER.error(errorMsg);
|
||||
throw new CloudRuntimeException(errorMsg);
|
||||
} else {
|
||||
LOGGER.info("[CLVM Migration] Successfully set volume [{}] to {} state.",
|
||||
LOGGER.info("Successfully set volume [{}] to {} state.",
|
||||
volumePath, stateDescription);
|
||||
}
|
||||
} catch (CloudRuntimeException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
String errorMsg = String.format(
|
||||
"[CLVM Migration] Exception while setting volume [%s] to %s state: %s",
|
||||
"Exception while setting volume [%s] to %s state: %s",
|
||||
volumePath, stateDescription, e.getMessage());
|
||||
LOGGER.error(errorMsg, e);
|
||||
throw new CloudRuntimeException(errorMsg, e);
|
||||
|
|
@ -6616,19 +6615,29 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||
public static void activateClvmVolumeExclusive(String volumePath) {
|
||||
modifyClvmVolumeState(volumePath, ClvmVolumeState.EXCLUSIVE.getLvchangeFlag(),
|
||||
ClvmVolumeState.EXCLUSIVE.getDescription(),
|
||||
"Activating CLVM volume in exclusive mode for copy");
|
||||
"Activating CLVM volume in exclusive mode");
|
||||
}
|
||||
|
||||
public static void deactivateClvmVolume(String volumePath) {
|
||||
try {
|
||||
modifyClvmVolumeState(volumePath, ClvmVolumeState.DEACTIVATE.getLvchangeFlag(),
|
||||
ClvmVolumeState.DEACTIVATE.getDescription(),
|
||||
"Deactivating CLVM volume after copy");
|
||||
"Deactivating CLVM volume");
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("Failed to deactivate CLVM volume {}: {}", volumePath, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static void setClvmVolumeToSharedMode(String volumePath) {
|
||||
try {
|
||||
modifyClvmVolumeState(volumePath, ClvmVolumeState.SHARED.getLvchangeFlag(),
|
||||
ClvmVolumeState.SHARED.getDescription(),
|
||||
"Setting CLVM volume to shared mode");
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("Failed to set CLVM volume {} to shared mode: {}", volumePath, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a disk is on a CLVM storage pool by checking the actual pool type from VirtualMachineTO.
|
||||
* This is the most reliable method as it uses CloudStack's own storage pool information.
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ public final class LibvirtResizeVolumeCommandWrapper extends CommandWrapper<Resi
|
|||
logger.debug("Resizing volume: " + path + ", from: " + toHumanReadableSize(currentSize) + ", to: " + toHumanReadableSize(newSize) + ", type: " + type + ", name: " + vmInstanceName + ", shrinkOk: " + shrinkOk);
|
||||
|
||||
/* libvirt doesn't support resizing (C)LVM devices, and corrupts QCOW2 in some scenarios, so we have to do these via qemu-img */
|
||||
if (pool.getType() != StoragePoolType.CLVM && pool.getType() != StoragePoolType.CLVM_NG
|
||||
if (pool.getType() != StoragePoolType.CLVM && pool.getType() != StoragePoolType.CLVM_NG
|
||||
&& pool.getType() != StoragePoolType.Linstor && pool.getType() != StoragePoolType.PowerFlex
|
||||
&& vol.getFormat() != PhysicalDiskFormat.QCOW2) {
|
||||
logger.debug("Volume " + path + " can be resized by libvirt. Asking libvirt to resize the volume.");
|
||||
|
|
|
|||
|
|
@ -1129,9 +1129,9 @@ public class KVMStorageProcessor implements StorageProcessor {
|
|||
} else {
|
||||
final Script command = new Script(_manageSnapshotPath, cmd.getWaitInMillSeconds(), logger);
|
||||
String backupPath;
|
||||
if (primaryPool.getType() == StoragePoolType.CLVM) {
|
||||
if (primaryPool.getType() == StoragePoolType.CLVM || primaryPool.getType() == StoragePoolType.CLVM_NG) {
|
||||
backupPath = snapshotDisk.getPath();
|
||||
logger.debug("Using snapshotDisk path for CLVM backup: " + backupPath);
|
||||
logger.debug("Using snapshotDisk path for CLVM/CLVM_NG backup: " + backupPath);
|
||||
} else {
|
||||
backupPath = isCreatedFromVmSnapshot ? snapshotDisk.getPath() : snapshot.getPath();
|
||||
}
|
||||
|
|
@ -1181,71 +1181,85 @@ public class KVMStorageProcessor implements StorageProcessor {
|
|||
}
|
||||
|
||||
/**
|
||||
* Delete a CLVM snapshot using comprehensive cleanup.
|
||||
* For CLVM, the snapshot path stored in DB is: /dev/vgname/volumeuuid/snapshotuuid
|
||||
* the actual snapshot LV created by CLVM is: /dev/vgname/md5(snapshotuuid)
|
||||
* Parse CLVM/CLVM_NG snapshot path and compute MD5 hash.
|
||||
* Snapshot path format: /dev/vgname/volumeuuid/snapshotuuid
|
||||
*
|
||||
* @param snapshotPath The snapshot path from database
|
||||
* @param poolType Storage pool type (for logging)
|
||||
* @return Array of [vgName, volumeUuid, snapshotUuid, md5Hash] or null if invalid
|
||||
*/
|
||||
private String[] parseClvmSnapshotPath(String snapshotPath, StoragePoolType poolType) {
|
||||
String[] pathParts = snapshotPath.split("/");
|
||||
if (pathParts.length < 5) {
|
||||
logger.warn("Invalid {} snapshot path format: {}, expected format: /dev/vgname/volume-uuid/snapshot-uuid",
|
||||
poolType, snapshotPath);
|
||||
return null;
|
||||
}
|
||||
|
||||
String vgName = pathParts[2];
|
||||
String volumeUuid = pathParts[3];
|
||||
String snapshotUuid = pathParts[4];
|
||||
|
||||
logger.info("Parsed {} snapshot path - VG: {}, Volume: {}, Snapshot: {}",
|
||||
poolType, vgName, volumeUuid, snapshotUuid);
|
||||
|
||||
String md5Hash = computeMd5Hash(snapshotUuid);
|
||||
logger.debug("Computed MD5 hash for snapshot UUID {}: {}", snapshotUuid, md5Hash);
|
||||
|
||||
return new String[]{vgName, volumeUuid, snapshotUuid, md5Hash};
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a CLVM or CLVM_NG snapshot using managesnapshot.sh script.
|
||||
* For both CLVM and CLVM_NG, the snapshot path stored in DB is: /dev/vgname/volumeuuid/snapshotuuid
|
||||
* The script handles MD5 transformation and pool-specific deletion commands internally:
|
||||
* - CLVM: Uses lvremove to delete LVM snapshot
|
||||
* - CLVM_NG: Uses qemu-img snapshot -d to delete QCOW2 internal snapshot
|
||||
* This approach is consistent with snapshot creation and backup which also use the script.
|
||||
*
|
||||
* @param snapshotPath The snapshot path from database
|
||||
* @param poolType Storage pool type (CLVM or CLVM_NG)
|
||||
* @param checkExistence If true, checks if snapshot exists before cleanup (for explicit deletion)
|
||||
* If false, always performs cleanup (for post-backup cleanup)
|
||||
* @return true if cleanup was performed, false if snapshot didn't exist (when checkExistence=true)
|
||||
*/
|
||||
private boolean deleteClvmSnapshot(String snapshotPath, boolean checkExistence) {
|
||||
logger.info("Starting CLVM snapshot deletion for path: {}, checkExistence: {}", snapshotPath, checkExistence);
|
||||
private boolean deleteClvmSnapshot(String snapshotPath, StoragePoolType poolType, boolean checkExistence) {
|
||||
logger.info("Starting {} snapshot deletion for path: {}, checkExistence: {}", poolType, snapshotPath, checkExistence);
|
||||
|
||||
try {
|
||||
// Parse the snapshot path: /dev/acsvg/volume-uuid/snapshot-uuid
|
||||
String[] pathParts = snapshotPath.split("/");
|
||||
if (pathParts.length < 5) {
|
||||
logger.warn("Invalid CLVM snapshot path format: {}, expected format: /dev/vgname/volume-uuid/snapshot-uuid", snapshotPath);
|
||||
String[] parsed = parseClvmSnapshotPath(snapshotPath, poolType);
|
||||
if (parsed == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String vgName = pathParts[2];
|
||||
String volumeUuid = pathParts[3];
|
||||
String snapshotUuid = pathParts[4];
|
||||
String vgName = parsed[0];
|
||||
String volumeUuid = parsed[1];
|
||||
String snapshotUuid = parsed[2];
|
||||
String volumePath = "/dev/" + vgName + "/" + volumeUuid;
|
||||
|
||||
logger.info("Parsed snapshot path - VG: {}, Volume: {}, Snapshot: {}", vgName, volumeUuid, snapshotUuid);
|
||||
// Use managesnapshot.sh script for deletion (consistent with create/backup)
|
||||
// Script handles MD5 transformation and pool-specific commands internally
|
||||
Script deleteCommand = new Script(_manageSnapshotPath, 10000, logger);
|
||||
deleteCommand.add("-d", volumePath);
|
||||
deleteCommand.add("-n", snapshotUuid);
|
||||
|
||||
// Compute MD5 hash of snapshot UUID (same as managesnapshot.sh does)
|
||||
String md5Hash = computeMd5Hash(snapshotUuid);
|
||||
logger.debug("Computed MD5 hash for snapshot UUID {}: {}", snapshotUuid, md5Hash);
|
||||
String snapshotLvPath = vgName + "/" + md5Hash;
|
||||
String actualSnapshotPath = "/dev/" + snapshotLvPath;
|
||||
logger.info("Executing: managesnapshot.sh -d {} -n {}", volumePath, snapshotUuid);
|
||||
String result = deleteCommand.execute();
|
||||
|
||||
// Check if snapshot exists (if requested)
|
||||
if (checkExistence) {
|
||||
Script checkSnapshot = new Script("/usr/sbin/lvs", 5000, logger);
|
||||
checkSnapshot.add("--noheadings");
|
||||
checkSnapshot.add(snapshotLvPath);
|
||||
String checkResult = checkSnapshot.execute();
|
||||
|
||||
if (checkResult != null) {
|
||||
// Snapshot doesn't exist - was already cleaned up
|
||||
logger.info("CLVM snapshot {} was already deleted, no cleanup needed", md5Hash);
|
||||
return false;
|
||||
}
|
||||
logger.info("CLVM snapshot still exists for {}, performing cleanup", md5Hash);
|
||||
}
|
||||
|
||||
// Use native LVM command to remove snapshot (handles all cleanup automatically)
|
||||
Script removeSnapshot = new Script("lvremove", 10000, logger);
|
||||
removeSnapshot.add("-f");
|
||||
removeSnapshot.add(snapshotLvPath);
|
||||
|
||||
logger.info("Executing: lvremove -f {}", snapshotLvPath);
|
||||
String removeResult = removeSnapshot.execute();
|
||||
|
||||
if (removeResult == null) {
|
||||
logger.info("Successfully deleted CLVM snapshot: {} (actual path: {})", snapshotPath, actualSnapshotPath);
|
||||
if (result == null) {
|
||||
logger.info("Successfully deleted {} snapshot: {}", poolType, snapshotPath);
|
||||
return true;
|
||||
} else {
|
||||
logger.warn("Failed to delete CLVM snapshot {}: {}", snapshotPath, removeResult);
|
||||
if (checkExistence && result.contains("does not exist")) {
|
||||
logger.info("{} snapshot {} already deleted, no cleanup needed", poolType, snapshotPath);
|
||||
return true;
|
||||
}
|
||||
logger.warn("Failed to delete {} snapshot {}: {}", poolType, snapshotPath, result);
|
||||
return false;
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
logger.error("Exception while deleting CLVM snapshot {}", snapshotPath, ex);
|
||||
logger.error("Exception while deleting {} snapshot {}", poolType, snapshotPath, ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1262,10 +1276,11 @@ public class KVMStorageProcessor implements StorageProcessor {
|
|||
|
||||
if ((backupSnapshotAfterTakingSnapshot == null || BooleanUtils.toBoolean(backupSnapshotAfterTakingSnapshot)) && deleteSnapshotOnPrimary) {
|
||||
try {
|
||||
if (primaryPool.getType() == StoragePoolType.CLVM) {
|
||||
boolean cleanedUp = deleteClvmSnapshot(snapshotPath, false);
|
||||
if (primaryPool.getType() == StoragePoolType.CLVM || primaryPool.getType() == StoragePoolType.CLVM_NG) {
|
||||
// Both CLVM and CLVM_NG use the same deletion method via managesnapshot.sh script
|
||||
boolean cleanedUp = deleteClvmSnapshot(snapshotPath, primaryPool.getType(), false);
|
||||
if (!cleanedUp) {
|
||||
logger.info("No need to delete CLVM snapshot on primary as it doesn't exist: {}", snapshotPath);
|
||||
logger.info("No need to delete {} snapshot on primary as it doesn't exist: {}", primaryPool.getType(), snapshotPath);
|
||||
}
|
||||
} else {
|
||||
Files.deleteIfExists(Paths.get(snapshotPath));
|
||||
|
|
@ -1637,6 +1652,10 @@ public class KVMStorageProcessor implements StorageProcessor {
|
|||
if (attachingDisk.getFormat() == PhysicalDiskFormat.QCOW2) {
|
||||
diskdef.setDiskFormatType(DiskDef.DiskFmtType.QCOW2);
|
||||
}
|
||||
} else if (attachingPool.getType() == StoragePoolType.CLVM_NG) {
|
||||
// CLVM_NG uses QCOW2 format on block devices
|
||||
diskdef.defBlockBasedDisk(attachingDisk.getPath(), devId, busT);
|
||||
diskdef.setDiskFormatType(DiskDef.DiskFmtType.QCOW2);
|
||||
} else if (attachingDisk.getFormat() == PhysicalDiskFormat.QCOW2) {
|
||||
diskdef.defFileBasedDisk(attachingDisk.getPath(), devId, busT, DiskDef.DiskFmtType.QCOW2);
|
||||
} else if (attachingDisk.getFormat() == PhysicalDiskFormat.RAW) {
|
||||
|
|
@ -1986,7 +2005,7 @@ public class KVMStorageProcessor implements StorageProcessor {
|
|||
if (snapshotSize != null) {
|
||||
newSnapshot.setPhysicalSize(snapshotSize);
|
||||
}
|
||||
} else if (primaryPool.getType() == StoragePoolType.CLVM) {
|
||||
} else if (primaryPool.getType() == StoragePoolType.CLVM || primaryPool.getType() == StoragePoolType.CLVM_NG) {
|
||||
CreateObjectAnswer result = takeClvmVolumeSnapshotOfStoppedVm(disk, snapshotName);
|
||||
if (result != null) return result;
|
||||
newSnapshot.setPath(snapshotPath);
|
||||
|
|
@ -2999,24 +3018,24 @@ public class KVMStorageProcessor implements StorageProcessor {
|
|||
if (snapshotTO.isKvmIncrementalSnapshot()) {
|
||||
deleteCheckpoint(snapshotTO);
|
||||
}
|
||||
} else if (primaryPool.getType() == StoragePoolType.CLVM) {
|
||||
// For CLVM, snapshots are typically already deleted from primary storage during backup
|
||||
} else if (primaryPool.getType() == StoragePoolType.CLVM || primaryPool.getType() == StoragePoolType.CLVM_NG) {
|
||||
// For CLVM/CLVM_NG, snapshots are typically already deleted from primary storage during backup
|
||||
// via deleteSnapshotOnPrimary in the backupSnapshot finally block.
|
||||
// This is called when the user explicitly deletes the snapshot via UI/API.
|
||||
// We check if the snapshot still exists and clean it up if needed.
|
||||
logger.info("Processing CLVM snapshot deletion (id={}, name={}, path={}) on primary storage",
|
||||
logger.info("Processing CLVM/CLVM_NG snapshot deletion (id={}, name={}, path={}) on primary storage",
|
||||
snapshotTO.getId(), snapshotTO.getName(), snapshotTO.getPath());
|
||||
|
||||
String snapshotPath = snapshotTO.getPath();
|
||||
if (snapshotPath != null && !snapshotPath.isEmpty()) {
|
||||
boolean wasDeleted = deleteClvmSnapshot(snapshotPath, true);
|
||||
boolean wasDeleted = deleteClvmSnapshot(snapshotPath, primaryPool.getType(), true);
|
||||
if (wasDeleted) {
|
||||
logger.info("Successfully cleaned up CLVM snapshot {} from primary storage", snapshotName);
|
||||
logger.info("Successfully cleaned up {} snapshot {} from primary storage", primaryPool.getType(), snapshotName);
|
||||
} else {
|
||||
logger.info("CLVM snapshot {} was already deleted from primary storage during backup, no cleanup needed", snapshotName);
|
||||
logger.info("{} snapshot {} was already deleted from primary storage during backup, no cleanup needed", primaryPool.getType(), snapshotName);
|
||||
}
|
||||
} else {
|
||||
logger.debug("CLVM snapshot path is null or empty, assuming already cleaned up");
|
||||
logger.debug("{} snapshot path is null or empty, assuming already cleaned up", primaryPool.getType());
|
||||
}
|
||||
} else {
|
||||
logger.warn("Operation not implemented for storage pool type of " + primaryPool.getType().toString());
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -65,6 +65,20 @@ get_lv() {
|
|||
lvm lvs --noheadings --unbuffered --separator=/ "${1}" | cut -d '/' -f 1 | tr -d ' '
|
||||
}
|
||||
|
||||
# Check if a block device contains QCOW2 data (CLVM_NG)
|
||||
is_qcow2_on_block_device() {
|
||||
local disk=$1
|
||||
|
||||
# Must be a block device
|
||||
if [ ! -b "${disk}" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if it contains QCOW2 data using qemu-img info
|
||||
${qemu_img} info "${disk}" 2>/dev/null | grep -q "file format: qcow2"
|
||||
return $?
|
||||
}
|
||||
|
||||
create_snapshot() {
|
||||
local disk=$1
|
||||
local snapshotname="$2"
|
||||
|
|
@ -73,7 +87,6 @@ create_snapshot() {
|
|||
islv_ret=$?
|
||||
|
||||
if [ ${dmsnapshot} = "yes" ] && [ "$islv_ret" == "1" ]; then
|
||||
# Modern LVM snapshot approach
|
||||
local lv=`get_lv ${disk}`
|
||||
local vg=`get_vg ${disk}`
|
||||
local lv_bytes=`blockdev --getsize64 ${disk}`
|
||||
|
|
@ -133,11 +146,18 @@ destroy_snapshot() {
|
|||
local disk=$1
|
||||
local snapshotname="$2"
|
||||
local failed=0
|
||||
|
||||
# If the disk path does not exist any more, assume volume was deleted and
|
||||
# the snapshot is already gone — return success to let caller proceed.
|
||||
if [ ! -e "${disk}" ]; then
|
||||
printf "Disk %s does not exist; assuming volume removed and snapshot %s is deleted\n" "${disk}" "${snapshotname}" >&2
|
||||
return 0
|
||||
fi
|
||||
|
||||
is_lv ${disk}
|
||||
islv_ret=$?
|
||||
|
||||
if [ "$islv_ret" == "1" ]; then
|
||||
# Modern LVM snapshot deletion
|
||||
local lv=`get_lv ${disk}`
|
||||
local vg=`get_vg ${disk}`
|
||||
|
||||
|
|
@ -147,7 +167,6 @@ destroy_snapshot() {
|
|||
return 0
|
||||
fi
|
||||
|
||||
# Remove the snapshot using native LVM command
|
||||
lvm lvremove -f "${vg}/${snapshotname}" >&2
|
||||
if [ $? -gt 0 ]; then
|
||||
printf "***Failed to remove LVM snapshot ${vg}/${snapshotname}\n" >&2
|
||||
|
|
@ -176,7 +195,6 @@ rollback_snapshot() {
|
|||
islv_ret=$?
|
||||
|
||||
if [ ${dmrollback} = "yes" ] && [ "$islv_ret" == "1" ]; then
|
||||
# Modern LVM snapshot merge (rollback)
|
||||
local lv=`get_lv ${disk}`
|
||||
local vg=`get_vg ${disk}`
|
||||
|
||||
|
|
@ -229,9 +247,11 @@ backup_snapshot() {
|
|||
is_lv ${disk}
|
||||
islv_ret=$?
|
||||
|
||||
# Both CLVM and CLVM_NG use LVM snapshot backup, but with different formats
|
||||
if [ ${dmsnapshot} = "yes" ] && [ "$islv_ret" == "1" ] ; then
|
||||
local vg=`get_vg ${disk}`
|
||||
local scriptdir=`dirname ${0}`
|
||||
local input_format="raw"
|
||||
|
||||
# Check if snapshot exists using native LVM command
|
||||
if ! lvm lvs "${vg}/${snapshotname}" > /dev/null 2>&1; then
|
||||
|
|
@ -239,13 +259,19 @@ backup_snapshot() {
|
|||
return 1
|
||||
fi
|
||||
|
||||
# Use native LVM path for backup
|
||||
qemuimg_ret=$($qemu_img convert $forceShareFlag -f raw -O qcow2 "/dev/${vg}/${snapshotname}" "${destPath}/${destName}" 2>&1)
|
||||
# Detect if this is CLVM_NG (QCOW2 on block device)
|
||||
if is_qcow2_on_block_device "${disk}"; then
|
||||
input_format="qcow2"
|
||||
printf "Detected CLVM_NG volume, using qcow2 format for backup\n" >&2
|
||||
fi
|
||||
|
||||
# Use native LVM path for backup with appropriate format
|
||||
qemuimg_ret=$($qemu_img convert $forceShareFlag -f ${input_format} -O qcow2 "/dev/${vg}/${snapshotname}" "${destPath}/${destName}" 2>&1)
|
||||
ret_code=$?
|
||||
if [ $ret_code -gt 0 ] && ([[ $qemuimg_ret == *"invalid option"*"'U'"* ]] || [[ $qemuimg_ret == *"unrecognized option"*"'-U'"* ]])
|
||||
then
|
||||
forceShareFlag=""
|
||||
$qemu_img convert $forceShareFlag -f raw -O qcow2 "/dev/${vg}/${snapshotname}" "${destPath}/${destName}"
|
||||
$qemu_img convert $forceShareFlag -f ${input_format} -O qcow2 "/dev/${vg}/${snapshotname}" "${destPath}/${destName}"
|
||||
ret_code=$?
|
||||
fi
|
||||
if [ $ret_code -gt 0 ]
|
||||
|
|
@ -308,8 +334,15 @@ revert_snapshot() {
|
|||
local snapshotPath=$1
|
||||
local destPath=$2
|
||||
local output_format="qcow2"
|
||||
|
||||
# Check if destination is a block device
|
||||
if [ -b "$destPath" ]; then
|
||||
output_format="raw"
|
||||
if is_qcow2_on_block_device "${destPath}"; then
|
||||
output_format="qcow2"
|
||||
printf "Detected CLVM_NG volume %s, preserving QCOW2 format for revert\n" "${destPath}" >&2
|
||||
else
|
||||
output_format="raw"
|
||||
fi
|
||||
fi
|
||||
|
||||
${qemu_img} convert -f qcow2 -O ${output_format} "$snapshotPath" "$destPath" || \
|
||||
|
|
|
|||
|
|
@ -1052,6 +1052,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
|
|||
params.put("managed", cmd.isManaged());
|
||||
params.put("capacityBytes", cmd.getCapacityBytes());
|
||||
params.put("capacityIops", cmd.getCapacityIops());
|
||||
params.put("scheme", uriParams.get("scheme"));
|
||||
if (MapUtils.isNotEmpty(uriParams)) {
|
||||
params.putAll(uriParams);
|
||||
}
|
||||
|
|
@ -1137,6 +1138,10 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
|
|||
if (isHostOrPathBlank) {
|
||||
throw new InvalidParameterValueException("host or path is null, should be gluster://hostname/volume");
|
||||
}
|
||||
} else if (scheme.equalsIgnoreCase("clvm") || scheme.equalsIgnoreCase("clvm_ng")) {
|
||||
if (storagePath == null) {
|
||||
throw new InvalidParameterValueException("path is null, should be " + scheme.toLowerCase() + "://localhost/volume-group-name");
|
||||
}
|
||||
}
|
||||
|
||||
String hostPath = null;
|
||||
|
|
|
|||
|
|
@ -1667,7 +1667,7 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
|
|||
if (backupSnapToSecondary) {
|
||||
if (!isKvmAndFileBasedStorage) {
|
||||
backupSnapshotToSecondary(payload.getAsyncBackup(), snapshotStrategy, snapshotOnPrimary, payload.getZoneIds(), payload.getStoragePoolIds());
|
||||
if (storagePool.getPoolType() == StoragePoolType.CLVM) {
|
||||
if (storagePool.getPoolType() == StoragePoolType.CLVM || storagePool.getPoolType() == StoragePoolType.CLVM_NG) {
|
||||
_snapshotStoreDao.removeBySnapshotStore(snapshotId, snapshotOnPrimary.getDataStore().getId(), snapshotOnPrimary.getDataStore().getRole());
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -863,7 +863,7 @@ export default {
|
|||
var vg = (values.volumegroup.substring(0, 1) !== '/') ? ('/' + values.volumegroup) : values.volumegroup
|
||||
url = this.clvmURL(vg)
|
||||
} else if (values.protocol === 'CLVM_NG') {
|
||||
var vg = (values.volumegroup.substring(0, 1) !== '/') ? ('/' + values.volumegroup) : values.volumegroup
|
||||
vg = (values.volumegroup.substring(0, 1) !== '/') ? ('/' + values.volumegroup) : values.volumegroup
|
||||
url = this.clvmNgURL(vg)
|
||||
} else if (values.protocol === 'RBD') {
|
||||
url = this.rbdURL(values.radosmonitor, values.radospool, values.radosuser, values.radossecret)
|
||||
|
|
|
|||
|
|
@ -638,6 +638,9 @@ public class UriUtils {
|
|||
if (url.startsWith("rbd://")) {
|
||||
return getRbdUrlInfo(url);
|
||||
}
|
||||
if (url.startsWith("clvm://") || url.startsWith("clvm_ng://")) {
|
||||
return getClvmUrlInfo(url);
|
||||
}
|
||||
URI uri = new URI(UriUtils.encodeURIComponent(url));
|
||||
return new UriInfo(uri.getScheme(), uri.getHost(), uri.getPath(), uri.getUserInfo(), uri.getPort());
|
||||
} catch (URISyntaxException e) {
|
||||
|
|
@ -675,6 +678,36 @@ public class UriUtils {
|
|||
}
|
||||
}
|
||||
|
||||
private static UriInfo getClvmUrlInfo(String url) {
|
||||
if (url == null || (!url.toLowerCase().startsWith("clvm://") && !url.toLowerCase().startsWith("clvm_ng://"))) {
|
||||
throw new CloudRuntimeException("CLVM URL must start with \"clvm://\" or \"clvm_ng://\"");
|
||||
}
|
||||
|
||||
String scheme;
|
||||
String remainder;
|
||||
if (url.toLowerCase().startsWith("clvm_ng://")) {
|
||||
scheme = "clvm_ng";
|
||||
remainder = url.substring(10);
|
||||
} else {
|
||||
scheme = "clvm";
|
||||
remainder = url.substring(7);
|
||||
}
|
||||
|
||||
int firstSlash = remainder.indexOf('/');
|
||||
String host = (firstSlash == -1) ? remainder : remainder.substring(0, firstSlash);
|
||||
String path = (firstSlash == -1) ? "/" : remainder.substring(firstSlash);
|
||||
|
||||
if (host.isEmpty()) {
|
||||
host = "localhost";
|
||||
}
|
||||
|
||||
while (path.startsWith("//")) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
|
||||
return new UriInfo(scheme, host, path, null, -1);
|
||||
}
|
||||
|
||||
public static boolean isUrlForCompressedFile(String url) {
|
||||
return UriUtils.COMPRESSION_FORMATS.stream().anyMatch(f -> url.toLowerCase().endsWith(f));
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue