diff --git a/plugins/storage/volume/linstor/CHANGELOG.md b/plugins/storage/volume/linstor/CHANGELOG.md index 7da3516955df..47d1ddeb06cf 100644 --- a/plugins/storage/volume/linstor/CHANGELOG.md +++ b/plugins/storage/volume/linstor/CHANGELOG.md @@ -5,22 +5,24 @@ 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). -## [2025-10-03] +## [2025-12-18] ### Changed +- Provide /dev/drbd/by-res/ resource paths to CloudStack for usage. + +## [2025-10-03] +### Changed - Revert qcow2 snapshot now use sparse/discard options to convert on thin devices. ## [2025-08-05] ### Fixed - - getVolumeStats wasn't correctly working if multiple Linstor clusters/primary storages are used. ## [2025-07-01] ### Fixed - - Regression in 4.19.3 and 4.21.0 with templates from snapshots ## [2025-05-07] @@ -31,25 +33,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [2025-03-13] ### Fixed - - Implemented missing delete datastore, to correctly cleanup on datastore removal ## [2025-02-21] ### Fixed - - Always try to delete cs-...-rst resource before doing a snapshot backup ## [2025-01-27] ### Fixed - - Use of multiple primary storages on the same linstor controller ## [2025-01-20] ### Fixed - - Volume snapshots on zfs used the wrong dataset path to hide/unhide snapdev ## [2024-12-19] @@ -60,13 +58,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [2024-12-13] ### Fixed - - Linstor heartbeat check now also ask linstor-controller if there is no connection between nodes ## [2024-12-11] ### Fixed - - Only set allow-two-primaries if a live migration is performed ## [2024-10-28] @@ -79,17 +75,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [2024-10-14] ### Added - - Support for ISO direct download to primary storage ## [2024-10-04] ### Added - - Enable qemu discard="unmap" for Linstor block disks ## [2024-08-27] ### Changed - - Allow two primaries(+protocol c) is now set on resource-connection level instead of rd diff --git a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java index a664e7ed03b9..1fe318e5163b 100644 --- a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java +++ b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java @@ -232,7 +232,7 @@ public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, Qemu makeResourceAvailable(api, foundRscName, false); if (!resources.isEmpty() && !resources.get(0).getVolumes().isEmpty()) { - final String devPath = resources.get(0).getVolumes().get(0).getDevicePath(); + final String devPath = LinstorUtil.getDevicePathFromResource(resources.get(0)); logger.info("Linstor: Created drbd device: " + devPath); final KVMPhysicalDisk kvmDisk = new KVMPhysicalDisk(devPath, name, pool); kvmDisk.setFormat(QemuImg.PhysicalDiskFormat.RAW); @@ -455,8 +455,9 @@ public boolean disconnectPhysicalDisk(Map volumeToDisconnect) private Optional getResourceByPathOrName( final List resources, String path) { return resources.stream() - .filter(rsc -> getLinstorRscName(path).equalsIgnoreCase(rsc.getName()) || rsc.getVolumes().stream() - .anyMatch(v -> path.equals(v.getDevicePath()))) + .filter(rsc -> getLinstorRscName(path).equalsIgnoreCase(rsc.getName()) || + path.equals(LinstorUtil.formatDrbdByResDevicePath(rsc.getName())) || + rsc.getVolumes().stream().anyMatch(v -> path.equals(v.getDevicePath()))) .findFirst(); } diff --git a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java index 9c3c9d32611e..4196c12b116d 100644 --- a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java +++ b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java @@ -264,6 +264,16 @@ public static boolean areResourcesDiskless(DevelopersApi api, String rscName, Co return false; } + /** + * Format the device path for DRBD resources. + * @param rscName + * @return + */ + public static String formatDrbdByResDevicePath(String rscName) + { + return String.format("/dev/drbd/by-res/%s/0", rscName); + } + /** * Try to get the device path for the given resource name. * This could be made a bit more direct after java-linstor api is fixed for layer data subtypes. @@ -283,12 +293,7 @@ public static String getDevicePath(DevelopersApi api, String rscName) throws Api null); for (ResourceWithVolumes rsc : resources) { if (!rsc.getVolumes().isEmpty()) { - // CloudStack resource always only have 1 volume - String devicePath = rsc.getVolumes().get(0).getDevicePath(); - if (devicePath != null && !devicePath.isEmpty()) { - LOGGER.debug("getDevicePath: {} -> {}", rscName, devicePath); - return devicePath; - } + return LinstorUtil.getDevicePathFromResource(rsc); } } @@ -297,6 +302,24 @@ public static String getDevicePath(DevelopersApi api, String rscName) throws Api throw new CloudRuntimeException("Linstor: " + errMsg); } + /** + * Check if the resource has DRBD or not and deliver the correct device path. + * @param rsc + * @return + */ + public static String getDevicePathFromResource(ResourceWithVolumes rsc) { + if (!rsc.getVolumes().isEmpty()) { + // CloudStack resource always only have 1 volume + if (rsc.getLayerObject().getDrbd() != null) { + return formatDrbdByResDevicePath(rsc.getName()); + } else { + return rsc.getVolumes().get(0).getDevicePath(); + } + } + throw new CloudRuntimeException( + String.format("getDevicePath: Resource %s/%s doesn't have volumes", rsc.getNodeName(), rsc.getName())); + } + public static ApiCallRcList applyAuxProps(DevelopersApi api, String rscName, String dispName, String vmName) throws ApiException {