Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -658,10 +658,16 @@ public Answer copyVolumeFromPrimaryToSecondary(final CopyCommand cmd) {
final String secondaryStorageUrl = nfsStore.getUrl();
KVMStoragePool secondaryStoragePool = null;

boolean srcConnected = false;
try {
final String volumeName = UUID.randomUUID().toString();

final String destVolumeName = volumeName + "." + ImageFormat.QCOW2.getFileExtension();
// the source volume may not be attached anywhere (e.g. a detached volume being
// migrated between pools); connect it so storage drivers that expose devices on
// demand (e.g. Linstor shared storage pools) provide the device for the copy
srcConnected = storagePoolMgr.connectPhysicalDisk(
primaryStore.getPoolType(), primaryStore.getUuid(), srcVolumePath, null);
final KVMPhysicalDisk volume = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), srcVolumePath);
volume.setFormat(PhysicalDiskFormat.valueOf(srcFormat.toString()));

Expand All @@ -680,6 +686,9 @@ public Answer copyVolumeFromPrimaryToSecondary(final CopyCommand cmd) {
} finally {
srcVol.clearPassphrase();
destVol.clearPassphrase();
if (srcConnected) {
storagePoolMgr.disconnectPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), srcVolumePath);
}
if (secondaryStoragePool != null) {
storagePoolMgr.deleteStoragePool(secondaryStoragePool.getType(), secondaryStoragePool.getUuid());
}
Expand Down
14 changes: 14 additions & 0 deletions plugins/storage/volume/linstor/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ All notable changes to Linstor CloudStack plugin will be documented in this file
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2026-07-31]

### Added

- Support for shared (thick LVM) storage pools: resources on a shared LUN are
activated on at most one node; live migration uses the LINSTOR 1.29
make-available/unmake-available API (with fallback to the previous handling
for older controllers), snapshots work on thick LVM pools, and resources of
stopped VMs are activated on demand for start, snapshot backup and revert

### Changed

- java-linstor client updated to 0.8.0 (LINSTOR REST API 1.29.0)

## [2026-06-24]

### Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,24 @@ private String zfsSnapdev(boolean hide, String zfsUrl) {
return script.execute();
}

private String lvmSetActive(boolean activate, String devMapperPath) {
// lvm resolves /dev/mapper/vg-lv names textually, works also for inactive LVs
Script script = new Script("lvchange", Duration.millis(30000));
if (activate) {
script.add("-ay");
script.add("-K"); // snapshot LVs carry the skip-activation flag
} else {
script.add("-an");
}
// never talk to dmeventd: registering the snapshot for monitoring can block lvchange
// indefinitely (and with it the whole LVM lock on the node); a full-size thick COW
// snapshot cannot overflow, so monitoring is not needed for the backup window
script.add("--monitor");
script.add("n");
script.add(devMapperPath);
return script.execute();
}

private String qemuShrink(String path, long sizeByte, long timeout) {
Script qemuImg = new Script("qemu-img", Duration.millis(timeout));
qemuImg.add("resize");
Expand Down Expand Up @@ -146,6 +164,7 @@ public CopyCmdAnswer execute(LinstorBackupSnapshotCommand cmd, LibvirtComputingR
final KVMStoragePoolManager storagePoolMgr = serverResource.getStoragePoolMgr();
KVMStoragePool linstorPool = storagePoolMgr.getStoragePool(Storage.StoragePoolType.Linstor, src.getDataStore().getUuid());
boolean zfsHidden = false;
String lvmSnapDev = null; // vg/lv we activated for the copy, deactivated on cleanup
String srcPath = src.getPath();

if (linstorPool == null) {
Expand All @@ -169,6 +188,12 @@ public CopyCmdAnswer execute(LinstorBackupSnapshotCommand cmd, LibvirtComputingR
return new CopyCmdAnswer("Unable to unhide zfs snapshot device.");
}
srcPath = "/dev/zvol/" + srcPath.substring(6);
} else if (srcPath.startsWith("/dev/mapper/") && !new File(srcPath).exists()) {
// thick LVM snapshot LVs are created inactive with the skip-activation flag
if (lvmSetActive(true, srcPath) != null) {
return new CopyCmdAnswer("Unable to activate LVM snapshot device " + srcPath);
}
lvmSnapDev = srcPath;
}

secondaryPool = storagePoolMgr.getStoragePoolByURI(dstDataStore.getUrl());
Expand Down Expand Up @@ -205,6 +230,9 @@ public CopyCmdAnswer execute(LinstorBackupSnapshotCommand cmd, LibvirtComputingR
if (zfsHidden) {
zfsSnapdev(true, src.getPath());
}
if (lvmSnapDev != null) {
lvmSetActive(false, lvmSnapDev);
}
}
}
}
Loading
Loading