From 93b95aa80ebac0cb5b850da44321a197176ce313 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Thu, 5 Mar 2026 21:40:21 +0100 Subject: [PATCH 01/16] KVM: Enable HA heartbeat on ShareMountPoint --- .../hypervisor/kvm/resource/KVMHAMonitor.java | 4 +- ...VMActivityOnStoragePoolCommandWrapper.java | 3 +- .../kvm/storage/KVMStoragePool.java | 3 + .../kvm/storage/KVMStoragePoolManager.java | 3 +- .../kvm/storage/LibvirtStoragePool.java | 13 +- .../CloudStackPrimaryDataStoreDriverImpl.java | 5 +- scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh | 218 ++++++++++++++++++ 7 files changed, 243 insertions(+), 6 deletions(-) create mode 100755 scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/KVMHAMonitor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/KVMHAMonitor.java index cf407bfc08a8..41c64629627c 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/KVMHAMonitor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/KVMHAMonitor.java @@ -34,6 +34,8 @@ public class KVMHAMonitor extends KVMHABase implements Runnable { + public static final List STORAGE_POOL_TYPES_WITH_HA_SUPPORT = List.of(StoragePoolType.NetworkFilesystem, StoragePoolType.SharedMountPoint); + private final Map storagePool = new ConcurrentHashMap<>(); private final boolean rebootHostAndAlertManagementOnHeartbeatTimeout; @@ -86,7 +88,7 @@ protected void runHeartBeat() { Set removedPools = new HashSet<>(); for (String uuid : storagePool.keySet()) { HAStoragePool primaryStoragePool = storagePool.get(uuid); - if (primaryStoragePool.getPool().getType() == StoragePoolType.NetworkFilesystem) { + if (STORAGE_POOL_TYPES_WITH_HA_SUPPORT.contains(primaryStoragePool.getPool().getType())) { checkForNotExistingPools(removedPools, uuid); if (removedPools.contains(uuid)) { continue; diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckVMActivityOnStoragePoolCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckVMActivityOnStoragePoolCommandWrapper.java index a708d441be59..b7a9882dc0a4 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckVMActivityOnStoragePoolCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckVMActivityOnStoragePoolCommandWrapper.java @@ -47,8 +47,9 @@ public Answer execute(final CheckVMActivityOnStoragePoolCommand command, final L final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr(); KVMStoragePool primaryPool = storagePoolMgr.getStoragePool(pool.getType(), pool.getUuid()); + primaryPool.setType(pool.getType()); - if (primaryPool.isPoolSupportHA()){ + if (primaryPool.isPoolSupportHA()) { final HAStoragePool nfspool = monitor.getStoragePool(pool.getUuid()); final KVMHAVMActivityChecker ha = new KVMHAVMActivityChecker(nfspool, command.getHost(), command.getVolumeList(), libvirtComputingResource.getVmActivityCheckPath(), command.getSuspectTimeInSeconds()); final Future future = executors.submit(ha); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePool.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePool.java index 8dd2116e1235..d1d30d21ef08 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePool.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePool.java @@ -91,6 +91,9 @@ default Long getUsedIops() { public StoragePoolType getType(); + default void setType(StoragePoolType type) { + } + public boolean delete(); PhysicalDiskFormat getDefaultFormat(); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java index 6665cf625e2f..9480271c09b2 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java @@ -57,7 +57,7 @@ public class KVMStoragePoolManager { private final Map _storagePools = new ConcurrentHashMap(); private final Map _storageMapper = new HashMap(); - private StorageAdaptor getStorageAdaptor(StoragePoolType type) { + public StorageAdaptor getStorageAdaptor(StoragePoolType type) { // type can be null: LibVirtComputingResource:3238 if (type == null) { return _storageMapper.get("libvirt"); @@ -390,6 +390,7 @@ public KVMStoragePool createStoragePool(String name, String host, int port, Stri private synchronized KVMStoragePool createStoragePool(String name, String host, int port, String path, String userInfo, StoragePoolType type, Map details, boolean primaryStorage) { StorageAdaptor adaptor = getStorageAdaptor(type); KVMStoragePool pool = adaptor.createStoragePool(name, host, port, path, userInfo, type, details, primaryStorage); + pool.setType(type); // LibvirtStorageAdaptor-specific statement if (pool.isPoolSupportHA() && primaryStorage) { diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java index ab39f7bc6ffd..4534b6b5eaf7 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java @@ -32,6 +32,7 @@ import com.cloud.agent.properties.AgentProperties; import com.cloud.agent.properties.AgentPropertiesFileHandler; import com.cloud.hypervisor.kvm.resource.KVMHABase.HAStoragePool; +import com.cloud.hypervisor.kvm.resource.KVMHAMonitor; import com.cloud.storage.Storage; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.utils.exception.CloudRuntimeException; @@ -320,13 +321,16 @@ public void setDetails(Map details) { @Override public boolean isPoolSupportHA() { - return type == StoragePoolType.NetworkFilesystem; + return KVMHAMonitor.STORAGE_POOL_TYPES_WITH_HA_SUPPORT.contains(type); } public String getHearthBeatPath() { - if (type == StoragePoolType.NetworkFilesystem) { + if (StoragePoolType.NetworkFilesystem.equals(type)) { String kvmScriptsDir = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.KVM_SCRIPTS_DIR); return Script.findScript(kvmScriptsDir, "kvmheartbeat.sh"); + } else if (StoragePoolType.SharedMountPoint.equals(type)) { + String kvmScriptsDir = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.KVM_SCRIPTS_DIR); + return Script.findScript(kvmScriptsDir, "kvmsmpheartbeat.sh"); } return null; } @@ -410,4 +414,9 @@ public Boolean vmActivityCheck(HAStoragePool pool, HostTO host, Duration activit return true; } } + + @Override + public void setType(StoragePoolType type) { + this.type = type; + } } diff --git a/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java b/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java index 5faa377ce3d3..e68fa9b3a31f 100644 --- a/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java @@ -21,6 +21,7 @@ import static com.cloud.utils.NumbersUtil.toHumanReadableSize; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.UUID; @@ -100,6 +101,8 @@ public Map getCapabilities() { protected Logger logger = LogManager.getLogger(getClass()); private static final String NO_REMOTE_ENDPOINT_WITH_ENCRYPTION = "No remote endpoint to send command, unable to find a valid endpoint. Requires encryption support: %s"; + private static final List STORAGE_POOL_TYPES_WITH_HA_SUPPORT = List.of(StoragePoolType.NetworkFilesystem, StoragePoolType.SharedMountPoint); + @Inject DiskOfferingDao diskOfferingDao; @Inject @@ -587,7 +590,7 @@ private boolean anyVolumeRequiresEncryption(DataObject ... objects) { @Override public boolean isStorageSupportHA(StoragePoolType type) { - return StoragePoolType.NetworkFilesystem == type; + return type != null && STORAGE_POOL_TYPES_WITH_HA_SUPPORT.contains(type); } @Override diff --git a/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh b/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh new file mode 100755 index 000000000000..fc430b272680 --- /dev/null +++ b/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh @@ -0,0 +1,218 @@ +#!/bin/bash +# 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. + +help() { + printf "Usage: $0 + -i identifier (ignored for local-only heartbeat) + -p path (ignored for local-only heartbeat) + -m mount point (local path where heartbeat will be written) + -h host (host IP/name to include in heartbeat filename) + -r write/read hb log (read-check mode) + -c cleanup (trigger emergency reboot) + -t interval between read hb log\n" + exit 1 +} + +#set -x +NfsSvrIP= +NfsSvrPath= +MountPoint= +HostIP= +interval= +rflag=0 +cflag=0 + +while getopts 'i:p:m:h:t:rc' OPTION +do + case $OPTION in + i) + NfsSvrIP="$OPTARG" + ;; # retained for CLI compatibility but unused for local-only script + p) + NfsSvrPath="$OPTARG" + ;; # retained for CLI compatibility but unused for local-only script + m) + MountPoint="$OPTARG" + ;; + h) + HostIP="$OPTARG" + ;; + r) + rflag=1 + ;; + t) + interval="$OPTARG" + ;; + c) + cflag=1 + ;; + *) + help + ;; + esac +done + +# Match original kvmheartbeat.sh: require NfsSvrIP parameter for CLI compatibility +if [ -z "$NfsSvrIP" ] +then + exit 1 +fi + +# For local-only heartbeat we require a mountpoint +if [ -z "$MountPoint" ] +then + echo "Mount point (-m) is required" + help +fi + +# Ensure mount point exists and is writable +if [ ! -d "$MountPoint" ]; then + mkdir -p "$MountPoint" 2>/dev/null + if [ $? -ne 0 ]; then + echo "Failed to create mount point directory: $MountPoint" >&2 + exit 1 + fi +fi + +# Determine a sensible HostIP if not provided +if [ -z "$HostIP" ]; then + # try to get a non-loopback IPv4 address, fallback to hostname + ipaddr=$(hostname -I 2>/dev/null | awk '{print $1}') + if [ -n "$ipaddr" ]; then + HostIP="$ipaddr" + else + HostIP=$(hostname) + fi +fi + +#delete VMs on this mountpoint (best-effort) +deleteVMs() { + local mountPoint=$1 + vmPids=$(ps aux | grep qemu | grep "$mountPoint" | awk '{print $2}' 2> /dev/null) + if [ $? -gt 0 ] + then + return + fi + + if [ -z "$vmPids" ] + then + return + fi + + for pid in $vmPids + do + kill -9 $pid &> /dev/null + done +} + +#checking is there the mount point present under $MountPoint? +mounts=$(cat /proc/mounts | grep "$MountPoint") +if [ $? -gt 0 ] +then + # mount point not present — we don't remount in local-only script + # nothing to do here; keep for compatibility with original flow + : +else + # mount exists; if not in read-check mode, consider deleting VMs similar to original behavior + if [ "$rflag" == "0" ] + then + deleteVMs $MountPoint + fi +fi + +hbFolder="$MountPoint/KVMHA/" +hbFile="$hbFolder/hb-$HostIP" + +write_hbLog() { +#write the heart beat log + stat "$hbFile" &> /dev/null + if [ $? -gt 0 ] + then + # create a new one + mkdir -p "$hbFolder" &> /dev/null + # touch will be done by atomic write below; ensure folder is writable + if [ ! -w "$hbFolder" ]; then + printf "Folder not writable: $hbFolder" >&2 + return 2 + fi + fi + + timestamp=$(date +%s) + # Write atomically to avoid partial writes (write to tmp then mv) + tmpfile="${hbFile}.$$" + printf "%s\n" "$timestamp" > "$tmpfile" 2>/dev/null + if [ $? -ne 0 ]; then + printf "Failed to write heartbeat to $tmpfile" >&2 + return 2 + fi + mv -f "$tmpfile" "$hbFile" 2>/dev/null + return $? +} + +check_hbLog() { + if [ ! -f "$hbFile" ]; then + # signal large difference if file missing + return 999999 + fi + now=$(date +%s) + hb=$(cat "$hbFile" 2>/dev/null) + if [ -z "$hb" ]; then + return 999998 + fi + diff=`expr $now - $hb 2>/dev/null` + if [ $? -ne 0 ] + then + return 999997 + fi + if [ -z "$interval" ]; then + # if no interval provided, consider 0 as success + if [ $diff -gt 0 ]; then + return $diff + else + return 0 + fi + fi + if [ $diff -gt $interval ] + then + return $diff + fi + return 0 +} + +if [ "$rflag" == "1" ] +then + check_hbLog + diff=$? + if [ $diff == 0 ] + then + echo "=====> ALIVE <=====" + else + echo "=====> Considering host as DEAD because last write on [$hbFile] was [$diff] seconds ago, but the max interval is [$interval] <======" + fi + exit 0 +elif [ "$cflag" == "1" ] +then + /usr/bin/logger -t heartbeat "kvmsmpheartbeat.sh will reboot system because it was unable to write the heartbeat to the storage." + sync & + sleep 5 + echo b > /proc/sysrq-trigger + exit $? +else + write_hbLog + exit $? +fi From f6c6a174ad424897a582fdff11d18afa5e08c157 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Tue, 10 Mar 2026 17:01:49 +0100 Subject: [PATCH 02/16] Update plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java --- .../com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java index 9480271c09b2..307f60bc9238 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java @@ -57,7 +57,7 @@ public class KVMStoragePoolManager { private final Map _storagePools = new ConcurrentHashMap(); private final Map _storageMapper = new HashMap(); - public StorageAdaptor getStorageAdaptor(StoragePoolType type) { + private StorageAdaptor getStorageAdaptor(StoragePoolType type) { // type can be null: LibVirtComputingResource:3238 if (type == null) { return _storageMapper.get("libvirt"); From a803c2f1b3fc6ebf0ccf8e2c603509fbe8e4350f Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Tue, 10 Mar 2026 17:05:31 +0100 Subject: [PATCH 03/16] Update scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh b/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh index fc430b272680..2bb34aed8f48 100755 --- a/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh +++ b/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh @@ -18,8 +18,8 @@ help() { printf "Usage: $0 - -i identifier (ignored for local-only heartbeat) - -p path (ignored for local-only heartbeat) + -i identifier (required for CLI compatibility; value ignored by local-only heartbeat) + -p path (required for CLI compatibility; value ignored by local-only heartbeat) -m mount point (local path where heartbeat will be written) -h host (host IP/name to include in heartbeat filename) -r write/read hb log (read-check mode) From eacece1681cc1a04ed41f6c59bb7630336aee313 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Tue, 10 Mar 2026 17:05:48 +0100 Subject: [PATCH 04/16] Update scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh b/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh index 2bb34aed8f48..e7f4527cb807 100755 --- a/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh +++ b/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh @@ -67,12 +67,6 @@ do esac done -# Match original kvmheartbeat.sh: require NfsSvrIP parameter for CLI compatibility -if [ -z "$NfsSvrIP" ] -then - exit 1 -fi - # For local-only heartbeat we require a mountpoint if [ -z "$MountPoint" ] then From 4e4da44a1fb62df557ccacfe6165acc0371f855d Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Thu, 12 Mar 2026 09:36:40 +0100 Subject: [PATCH 05/16] move STORAGE_POOL_TYPES_WITH_HA_SUPPORT to HighAvailabilityManager and rename it --- .../java/com/cloud/ha/HighAvailabilityManager.java | 3 +++ .../cloud/hypervisor/kvm/resource/KVMHAMonitor.java | 10 ++++------ .../hypervisor/kvm/storage/LibvirtStoragePool.java | 4 ++-- .../driver/CloudStackPrimaryDataStoreDriverImpl.java | 6 ++---- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/engine/components-api/src/main/java/com/cloud/ha/HighAvailabilityManager.java b/engine/components-api/src/main/java/com/cloud/ha/HighAvailabilityManager.java index ddc8153d7398..3ae94479cea5 100644 --- a/engine/components-api/src/main/java/com/cloud/ha/HighAvailabilityManager.java +++ b/engine/components-api/src/main/java/com/cloud/ha/HighAvailabilityManager.java @@ -21,6 +21,7 @@ import com.cloud.deploy.DeploymentPlanner; import com.cloud.host.HostVO; import com.cloud.host.Status; +import com.cloud.storage.Storage.StoragePoolType; import com.cloud.utils.component.Manager; import com.cloud.vm.VMInstanceVO; import org.apache.cloudstack.framework.config.ConfigKey; @@ -32,6 +33,8 @@ */ public interface HighAvailabilityManager extends Manager { + List LIBVIRT_STORAGE_POOL_TYPES_WITH_HA_SUPPORT = List.of(StoragePoolType.NetworkFilesystem, StoragePoolType.SharedMountPoint); + ConfigKey ForceHA = new ConfigKey<>("Advanced", Boolean.class, "force.ha", "false", "Force High-Availability to happen even if the VM says no.", true, Cluster); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/KVMHAMonitor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/KVMHAMonitor.java index 41c64629627c..a224438b7078 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/KVMHAMonitor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/KVMHAMonitor.java @@ -18,7 +18,7 @@ import com.cloud.agent.properties.AgentProperties; import com.cloud.agent.properties.AgentPropertiesFileHandler; -import com.cloud.storage.Storage.StoragePoolType; +import com.cloud.ha.HighAvailabilityManager; import com.cloud.utils.script.Script; import org.libvirt.Connect; import org.libvirt.LibvirtException; @@ -34,8 +34,6 @@ public class KVMHAMonitor extends KVMHABase implements Runnable { - public static final List STORAGE_POOL_TYPES_WITH_HA_SUPPORT = List.of(StoragePoolType.NetworkFilesystem, StoragePoolType.SharedMountPoint); - private final Map storagePool = new ConcurrentHashMap<>(); private final boolean rebootHostAndAlertManagementOnHeartbeatTimeout; @@ -88,8 +86,8 @@ protected void runHeartBeat() { Set removedPools = new HashSet<>(); for (String uuid : storagePool.keySet()) { HAStoragePool primaryStoragePool = storagePool.get(uuid); - if (STORAGE_POOL_TYPES_WITH_HA_SUPPORT.contains(primaryStoragePool.getPool().getType())) { - checkForNotExistingPools(removedPools, uuid); + if (HighAvailabilityManager.LIBVIRT_STORAGE_POOL_TYPES_WITH_HA_SUPPORT.contains(primaryStoragePool.getPool().getType())) { + checkForNotExistingLibvirtStoragePools(removedPools, uuid); if (removedPools.contains(uuid)) { continue; } @@ -129,7 +127,7 @@ private String executePoolHeartBeatCommand(String uuid, HAStoragePool primarySto return result; } - private void checkForNotExistingPools(Set removedPools, String uuid) { + private void checkForNotExistingLibvirtStoragePools(Set removedPools, String uuid) { try { Connect conn = LibvirtConnection.getConnection(); StoragePool storage = conn.storagePoolLookupByUUIDString(uuid); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java index 4534b6b5eaf7..97ac799badd9 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java @@ -31,8 +31,8 @@ import com.cloud.agent.api.to.HostTO; import com.cloud.agent.properties.AgentProperties; import com.cloud.agent.properties.AgentPropertiesFileHandler; +import com.cloud.ha.HighAvailabilityManager; import com.cloud.hypervisor.kvm.resource.KVMHABase.HAStoragePool; -import com.cloud.hypervisor.kvm.resource.KVMHAMonitor; import com.cloud.storage.Storage; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.utils.exception.CloudRuntimeException; @@ -321,7 +321,7 @@ public void setDetails(Map details) { @Override public boolean isPoolSupportHA() { - return KVMHAMonitor.STORAGE_POOL_TYPES_WITH_HA_SUPPORT.contains(type); + return HighAvailabilityManager.LIBVIRT_STORAGE_POOL_TYPES_WITH_HA_SUPPORT.contains(type); } public String getHearthBeatPath() { diff --git a/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java b/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java index e68fa9b3a31f..6a7f1d580436 100644 --- a/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java @@ -21,13 +21,13 @@ import static com.cloud.utils.NumbersUtil.toHumanReadableSize; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.UUID; import javax.inject.Inject; import com.cloud.agent.api.to.DiskTO; +import com.cloud.ha.HighAvailabilityManager; import com.cloud.storage.VolumeVO; import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo; @@ -101,8 +101,6 @@ public Map getCapabilities() { protected Logger logger = LogManager.getLogger(getClass()); private static final String NO_REMOTE_ENDPOINT_WITH_ENCRYPTION = "No remote endpoint to send command, unable to find a valid endpoint. Requires encryption support: %s"; - private static final List STORAGE_POOL_TYPES_WITH_HA_SUPPORT = List.of(StoragePoolType.NetworkFilesystem, StoragePoolType.SharedMountPoint); - @Inject DiskOfferingDao diskOfferingDao; @Inject @@ -590,7 +588,7 @@ private boolean anyVolumeRequiresEncryption(DataObject ... objects) { @Override public boolean isStorageSupportHA(StoragePoolType type) { - return type != null && STORAGE_POOL_TYPES_WITH_HA_SUPPORT.contains(type); + return type != null && HighAvailabilityManager.LIBVIRT_STORAGE_POOL_TYPES_WITH_HA_SUPPORT.contains(type); } @Override From 5443527fcd2c42c37667585b08ec42397a828b2b Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Thu, 12 Mar 2026 10:23:13 +0100 Subject: [PATCH 06/16] Update scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh b/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh index e7f4527cb807..76618cc249b1 100755 --- a/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh +++ b/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh @@ -115,18 +115,17 @@ deleteVMs() { } #checking is there the mount point present under $MountPoint? -mounts=$(cat /proc/mounts | grep "$MountPoint") -if [ $? -gt 0 ] +if grep -q "^[^ ]\+ $MountPoint " /proc/mounts then - # mount point not present — we don't remount in local-only script - # nothing to do here; keep for compatibility with original flow - : -else # mount exists; if not in read-check mode, consider deleting VMs similar to original behavior if [ "$rflag" == "0" ] then deleteVMs $MountPoint fi +else + # mount point not present — we don't remount in local-only script + # nothing to do here; keep for compatibility with original flow + : fi hbFolder="$MountPoint/KVMHA/" From bca02216e6323311a4c24fa147b71c7116823dac Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Thu, 12 Mar 2026 10:27:43 +0100 Subject: [PATCH 07/16] Update scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh | 23 ++++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh b/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh index 76618cc249b1..5cd52100f514 100755 --- a/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh +++ b/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh @@ -158,40 +158,49 @@ write_hbLog() { } check_hbLog() { + hb_diff=0 if [ ! -f "$hbFile" ]; then # signal large difference if file missing - return 999999 + hb_diff=999999 + return 1 fi now=$(date +%s) hb=$(cat "$hbFile" 2>/dev/null) if [ -z "$hb" ]; then - return 999998 + hb_diff=999998 + return 1 fi diff=`expr $now - $hb 2>/dev/null` if [ $? -ne 0 ] then - return 999997 + hb_diff=999997 + return 1 fi if [ -z "$interval" ]; then # if no interval provided, consider 0 as success if [ $diff -gt 0 ]; then - return $diff + hb_diff=$diff + return 1 else + hb_diff=0 return 0 fi fi if [ $diff -gt $interval ] then - return $diff + hb_diff=$diff + return 1 fi + hb_diff=0 return 0 } if [ "$rflag" == "1" ] then check_hbLog - diff=$? - if [ $diff == 0 ] + status=$? + diff="${hb_diff:-0}" + if [ $status -eq 0 ] then echo "=====> ALIVE <=====" else From c9b0f973265f81c1f8371e3dfcdb8e0b56d3ec2c Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Thu, 12 Mar 2026 10:28:08 +0100 Subject: [PATCH 08/16] Update scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh b/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh index 5cd52100f514..9882e2b7ee47 100755 --- a/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh +++ b/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh @@ -98,10 +98,6 @@ fi deleteVMs() { local mountPoint=$1 vmPids=$(ps aux | grep qemu | grep "$mountPoint" | awk '{print $2}' 2> /dev/null) - if [ $? -gt 0 ] - then - return - fi if [ -z "$vmPids" ] then From 1268f5d35048200e3f5c242aefeda58cc7930bc6 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Thu, 12 Mar 2026 10:29:06 +0100 Subject: [PATCH 09/16] minor changes --- scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh b/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh index 9882e2b7ee47..57ba78207691 100755 --- a/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh +++ b/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh @@ -42,10 +42,10 @@ do case $OPTION in i) NfsSvrIP="$OPTARG" - ;; # retained for CLI compatibility but unused for local-only script + ;; # retained for CLI compatibility but unused for this script p) NfsSvrPath="$OPTARG" - ;; # retained for CLI compatibility but unused for local-only script + ;; # retained for CLI compatibility but unused for this script m) MountPoint="$OPTARG" ;; @@ -67,7 +67,7 @@ do esac done -# For local-only heartbeat we require a mountpoint +# For heartbeat we require a mountpoint if [ -z "$MountPoint" ] then echo "Mount point (-m) is required" @@ -83,17 +83,6 @@ if [ ! -d "$MountPoint" ]; then fi fi -# Determine a sensible HostIP if not provided -if [ -z "$HostIP" ]; then - # try to get a non-loopback IPv4 address, fallback to hostname - ipaddr=$(hostname -I 2>/dev/null | awk '{print $1}') - if [ -n "$ipaddr" ]; then - HostIP="$ipaddr" - else - HostIP=$(hostname) - fi -fi - #delete VMs on this mountpoint (best-effort) deleteVMs() { local mountPoint=$1 From 8670f251a8e382dd01580a2b9345cfb560396165 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Thu, 12 Mar 2026 11:01:52 +0100 Subject: [PATCH 10/16] Update plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../hypervisor/kvm/storage/LibvirtStoragePool.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java index 97ac799badd9..241d37a038ea 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java @@ -327,10 +327,18 @@ public boolean isPoolSupportHA() { public String getHearthBeatPath() { if (StoragePoolType.NetworkFilesystem.equals(type)) { String kvmScriptsDir = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.KVM_SCRIPTS_DIR); - return Script.findScript(kvmScriptsDir, "kvmheartbeat.sh"); + String scriptPath = Script.findScript(kvmScriptsDir, "kvmheartbeat.sh"); + if (scriptPath == null) { + throw new CloudRuntimeException("Unable to find heartbeat script 'kvmheartbeat.sh' in directory: " + kvmScriptsDir); + } + return scriptPath; } else if (StoragePoolType.SharedMountPoint.equals(type)) { String kvmScriptsDir = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.KVM_SCRIPTS_DIR); - return Script.findScript(kvmScriptsDir, "kvmsmpheartbeat.sh"); + String scriptPath = Script.findScript(kvmScriptsDir, "kvmsmpheartbeat.sh"); + if (scriptPath == null) { + throw new CloudRuntimeException("Unable to find heartbeat script 'kvmsmpheartbeat.sh' in directory: " + kvmScriptsDir); + } + return scriptPath; } return null; } From b5436903e9c37ce257206655a06b3f50331f16a3 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Thu, 12 Mar 2026 11:02:43 +0100 Subject: [PATCH 11/16] Update scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh b/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh index 57ba78207691..96010736bd2a 100755 --- a/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh +++ b/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh @@ -113,7 +113,7 @@ else : fi -hbFolder="$MountPoint/KVMHA/" +hbFolder="$MountPoint/KVMHA" hbFile="$hbFolder/hb-$HostIP" write_hbLog() { From 67d9c8f1a0095fab50c08898ee3a35ef851de354 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Thu, 12 Mar 2026 11:27:10 +0100 Subject: [PATCH 12/16] fix wrong logic --- scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh b/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh index 96010736bd2a..22c76b14c90c 100755 --- a/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh +++ b/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh @@ -102,15 +102,15 @@ deleteVMs() { #checking is there the mount point present under $MountPoint? if grep -q "^[^ ]\+ $MountPoint " /proc/mounts then - # mount exists; if not in read-check mode, consider deleting VMs similar to original behavior + # mount exists; nothing to do here; keep for compatibility with original flow + : +else + # mount point not present + # if not in read-check mode, consider deleting VMs similar to original behavior if [ "$rflag" == "0" ] then deleteVMs $MountPoint fi -else - # mount point not present — we don't remount in local-only script - # nothing to do here; keep for compatibility with original flow - : fi hbFolder="$MountPoint/KVMHA" From 6bcb4591340f416e6d7c25a4c5f8280175af1a2b Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Thu, 12 Mar 2026 11:41:56 +0100 Subject: [PATCH 13/16] KVM: remove KVMHABase.s_heartBeatPath --- .../java/com/cloud/hypervisor/kvm/resource/KVMHABase.java | 1 - .../com/cloud/hypervisor/kvm/resource/KVMHAMonitor.java | 7 +------ .../hypervisor/kvm/resource/LibvirtComputingResource.java | 7 +------ 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/KVMHABase.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/KVMHABase.java index 896426addca1..e9a7ac8951ce 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/KVMHABase.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/KVMHABase.java @@ -35,7 +35,6 @@ public class KVMHABase { protected Logger logger = LogManager.getLogger(getClass()); private long _timeout = 60000; /* 1 minutes */ - protected static String s_heartBeatPath; protected long _heartBeatUpdateTimeout = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.HEARTBEAT_UPDATE_TIMEOUT); protected long _heartBeatUpdateFreq = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.KVM_HEARTBEAT_UPDATE_FREQUENCY); protected long _heartBeatUpdateMaxTries = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.KVM_HEARTBEAT_UPDATE_MAX_TRIES); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/KVMHAMonitor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/KVMHAMonitor.java index a224438b7078..aa868ff1d3f2 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/KVMHAMonitor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/KVMHAMonitor.java @@ -39,20 +39,15 @@ public class KVMHAMonitor extends KVMHABase implements Runnable { private final String hostPrivateIp; - public KVMHAMonitor(HAStoragePool pool, String host, String scriptPath) { + public KVMHAMonitor(HAStoragePool pool, String host) { if (pool != null) { storagePool.put(pool.getPoolUUID(), pool); } hostPrivateIp = host; - configureHeartBeatPath(scriptPath); rebootHostAndAlertManagementOnHeartbeatTimeout = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.REBOOT_HOST_AND_ALERT_MANAGEMENT_ON_HEARTBEAT_TIMEOUT); } - private static synchronized void configureHeartBeatPath(String scriptPath) { - KVMHABase.s_heartBeatPath = scriptPath; - } - public void addStoragePool(HAStoragePool pool) { synchronized (storagePool) { storagePool.put(pool.getPoolUUID(), pool); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index b561cedd0183..64df98f413a6 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -1063,11 +1063,6 @@ public boolean configure(final String name, final Map params) th throw new ConfigurationException("Unable to find patch.sh"); } - heartBeatPath = Script.findScript(kvmScriptsDir, "kvmheartbeat.sh"); - if (heartBeatPath == null) { - throw new ConfigurationException("Unable to find kvmheartbeat.sh"); - } - createVmPath = Script.findScript(storageScriptsDir, "createvm.sh"); if (createVmPath == null) { throw new ConfigurationException("Unable to find the createvm.sh"); @@ -1330,7 +1325,7 @@ public boolean configure(final String name, final Map params) th final String[] info = NetUtils.getNetworkParams(privateNic); - kvmhaMonitor = new KVMHAMonitor(null, info[0], heartBeatPath); + kvmhaMonitor = new KVMHAMonitor(null, info[0]); final Thread ha = new Thread(kvmhaMonitor); ha.start(); From d30ca6ebf85c2e507a642a11a501114fa70500df Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Thu, 12 Mar 2026 11:58:46 +0100 Subject: [PATCH 14/16] small refactoring --- .../LibvirtCheckVMActivityOnStoragePoolCommandWrapper.java | 1 - .../com/cloud/hypervisor/kvm/storage/KVMStoragePool.java | 3 --- .../cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java | 5 ++++- .../com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java | 1 - 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckVMActivityOnStoragePoolCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckVMActivityOnStoragePoolCommandWrapper.java index b7a9882dc0a4..d3f537dc9173 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckVMActivityOnStoragePoolCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckVMActivityOnStoragePoolCommandWrapper.java @@ -47,7 +47,6 @@ public Answer execute(final CheckVMActivityOnStoragePoolCommand command, final L final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr(); KVMStoragePool primaryPool = storagePoolMgr.getStoragePool(pool.getType(), pool.getUuid()); - primaryPool.setType(pool.getType()); if (primaryPool.isPoolSupportHA()) { final HAStoragePool nfspool = monitor.getStoragePool(pool.getUuid()); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePool.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePool.java index d1d30d21ef08..8dd2116e1235 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePool.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePool.java @@ -91,9 +91,6 @@ default Long getUsedIops() { public StoragePoolType getType(); - default void setType(StoragePoolType type) { - } - public boolean delete(); PhysicalDiskFormat getDefaultFormat(); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java index 307f60bc9238..35cc864268c3 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java @@ -289,6 +289,7 @@ public KVMStoragePool getStoragePool(StoragePoolType type, String uuid, boolean if (pool instanceof LibvirtStoragePool) { addPoolDetails(uuid, (LibvirtStoragePool) pool); + ((LibvirtStoragePool) pool).setType(type); } return pool; @@ -390,7 +391,9 @@ public KVMStoragePool createStoragePool(String name, String host, int port, Stri private synchronized KVMStoragePool createStoragePool(String name, String host, int port, String path, String userInfo, StoragePoolType type, Map details, boolean primaryStorage) { StorageAdaptor adaptor = getStorageAdaptor(type); KVMStoragePool pool = adaptor.createStoragePool(name, host, port, path, userInfo, type, details, primaryStorage); - pool.setType(type); + if (pool instanceof LibvirtStoragePool) { + ((LibvirtStoragePool) pool).setType(type); + } // LibvirtStorageAdaptor-specific statement if (pool.isPoolSupportHA() && primaryStorage) { diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java index 241d37a038ea..45c22d3ac754 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java @@ -423,7 +423,6 @@ public Boolean vmActivityCheck(HAStoragePool pool, HostTO host, Duration activit } } - @Override public void setType(StoragePoolType type) { this.type = type; } From ab4557d1607e6a015f6bc7d76dfbe5b0154f2506 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Thu, 12 Mar 2026 12:54:49 +0100 Subject: [PATCH 15/16] Update scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh b/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh index 22c76b14c90c..74392b1a1d8b 100755 --- a/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh +++ b/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh @@ -74,15 +74,25 @@ then help fi -# Ensure mount point exists and is writable +# Validate mount point exists, is (if possible) a mounted filesystem, and is writable if [ ! -d "$MountPoint" ]; then - mkdir -p "$MountPoint" 2>/dev/null - if [ $? -ne 0 ]; then - echo "Failed to create mount point directory: $MountPoint" >&2 + echo "Mount point directory does not exist: $MountPoint" >&2 + exit 1 +fi + +# If the 'mountpoint' utility is available, ensure this is an actual mount +if command -v mountpoint >/dev/null 2>&1; then + if ! mountpoint -q "$MountPoint"; then + echo "Mount point is not a mounted filesystem: $MountPoint" >&2 exit 1 fi fi +# Ensure the mount point is writable +if [ ! -w "$MountPoint" ]; then + echo "Mount point is not writable: $MountPoint" >&2 + exit 1 +fi #delete VMs on this mountpoint (best-effort) deleteVMs() { local mountPoint=$1 From a361b282e59c0b1c9b6abd174565b7da83366b87 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Thu, 12 Mar 2026 12:40:24 +0100 Subject: [PATCH 16/16] ensure mountPoint ends with a single trailing slash when delete vms on it --- scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh b/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh index 74392b1a1d8b..b102a1a866bb 100755 --- a/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh +++ b/scripts/vm/hypervisor/kvm/kvmsmpheartbeat.sh @@ -96,6 +96,9 @@ fi #delete VMs on this mountpoint (best-effort) deleteVMs() { local mountPoint=$1 + # ensure it ends with a single trailing slash + mountPoint="${mountPoint%/}/" + vmPids=$(ps aux | grep qemu | grep "$mountPoint" | awk '{print $2}' 2> /dev/null) if [ -z "$vmPids" ]