Skip to content

kvm: support Host HA on Ceph RBD primary storage - #14320

Open
weizhouapache wants to merge 28 commits into
apache:mainfrom
weizhouapache:24-kvm-ha-on-ceph
Open

weizhouapache wants to merge 28 commits into
apache:mainfrom
weizhouapache:24-kvm-ha-on-ceph

Conversation

@weizhouapache

@weizhouapache weizhouapache commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

Description

Extend the existing KVM HA heartbeat and VM-activity-check framework (until now limited to NetworkFilesystem and SharedMountPoint pools) to Ceph RBD primary storage. It is based on the approach of the old PR #5862, adapted to the current HA architecture, and it reuses the multi-monitor Ceph support added in #6792.

How it works

  • StoragePoolType.RBD is added to LIBVIRT_STORAGE_POOL_TYPES_WITH_HA_SUPPORT, the single switch which makes pool registration, KVMHAMonitor and the CheckOnHostCommand / CheckVMActivityOnStoragePoolCommand wrappers treat RBD pools as HA-capable.
  • RBD has no shared mount point to write a heartbeat file to, so each host writes its heartbeat timestamp to a small RADOS object in the pool, KVMHA-hb-<host GUID>, and the state of the VM-activity check of Host HA to KVMHA-ac-<host GUID>. The objects are named after the GUID of the host as the management server knows it (the one of host.guid), not after the private IP. NFS and SharedMountPoint pools keep using the private IP.
  • The scripts are written in Python and use the Python bindings of librados and librbd (python3-rados, python3-rbd):
    • kvmha_rbd.py is the shared access to Ceph: one connection, operations which time out after 30 seconds, and nothing written to the standard error output.
    • kvmheartbeat_rbd.py writes the heartbeat, reads it with -r, and reboots the host with -c.
    • kvmvmactivity_rbd.py is the VM-activity check of Host HA: a fresh heartbeat means the host is alive, else the RBD watchers of the volumes of the host are looked at.
  • The Ceph key is not an argument of the scripts, as it would be visible in the process list: the agent gives it in the environment variable CEPH_KEY, and only together with the Ceph user. The monitors, the pool and the user are arguments; the monitor list is built with RbdUtils, which handles IPv6.
  • A host is reported as dead only if its heartbeat object exists and is older than the interval (kvm.heartbeat.update.frequency) plus heartbeat.update.timeout, the time which the agent lets a write take. A missing or malformed object, or a cluster which can not be reached, is "unable to tell" (exit status 2), never dead. With several pools, KVMHAChecker reports a host as dead if its heartbeat stopped on all of them, or on one of them if kvm.ha.fence.on.storage.heartbeat.failure is set. A host which monitors no pool can not tell, and does not report another host as dead.
  • The Python bindings are only suggested by the packages (Suggests in debian/control and in the el8 and suse15 specs), as they are not available on all the supported distributions. When a pool is registered, the agent runs kvmheartbeat_rbd.py --check-bindings. If the bindings are missing, or python3-rbd is too old to list the watchers of a volume, the pool is not monitored for HA on that host and an error naming the missing package is logged. That host then writes no heartbeat, so it can not fence itself, but it can not tell either how the other hosts are doing.
  • VM HA (hosts without out-of-band management) relies on the heartbeat only: KVMInvestigator sends CheckOnHostCommand, which KVMHAChecker answers from the age of the heartbeat, as it does for NFS. On an RBD-only cluster without OOBM this is all that decides. Host HA, which needs OOBM, also runs the VM-activity check on the watchers of the volumes before the host is fenced.
  • KVMHAMonitor keeps going if the heartbeat of one pool can not be started: that pool fails alone, and the heartbeats of the other pools go on.
  • KVMStoragePoolManager removes a pool from the HA monitor before it deletes it, so that no heartbeat is written while the pool is removed, and puts it back if the delete does not succeed. RBD and SharedMountPoint pools are still deleted with the delete which takes the details, as on main, so that the libvirt pool is not destroyed.

cwiki: https://cwiki.apache.org/confluence/spaces/CLOUDSTACK/pages/451979203/Host+HA+support+for+Ceph+RBD+primary+storage+KVM

Types of changes

  • Breaking change (fix or feature that would cause existing functionality to change)
  • New feature (non-breaking change which adds functionality)
  • Bug fix (non-breaking change which fixes an issue)
  • Enhancement (improves an existing feature and functionality)
  • Cleanup (Code refactoring and cleanup, that may add test cases)
  • Build/CI
  • Test (unit or integration test code)

Feature/Enhancement Scale or Bug Severity

Feature/Enhancement Scale

  • Major
  • Minor

Bug Severity

  • BLOCKER
  • Critical
  • Major
  • Minor
  • Trivial

Screenshots (if appropriate):

How Has This Been Tested?

  • Unit tests: new KVMHACheckerTest, KVMHAMonitorTest, KVMStoragePoolManagerTest and LibvirtCheckVMActivityOnStoragePoolCommandWrapperTest, and more cases in LibvirtStoragePoolTest and RbdUtilsTest.
  • The scripts were also run against stand-in rados and rbd modules, for the cases which are hard to produce on a real cluster: missing bindings, a cluster which can not be reached, a missing, malformed or stale heartbeat, and the age limits.
  • In a nested environment (Ubuntu 26.04 KVM hosts, libvirt 12.0, Ceph 20.2 client packages, two RBD primary storage pools), with the same changes on the 4.23 branch:
    • test/integration/smoke/test_hostha_kvm.py passes (9 of 9).
    • With force.ha set, a KVM host was force-stopped: it was marked Down after about 3.5 minutes, and its two Instances were started on the other host.
    • Reading the heartbeat of a host which has none exits with 2 ("unable to tell"), and reading a stale one reports the host as dead.

How did you try to break this feature and the system with this change?

  • Killed the agent of a host and force-stopped a whole host (see above).
  • Hosts without the Python bindings: the pool is not monitored on that host, with an error in the agent log, and the host writes no heartbeat which could fail.
  • A Ceph cluster which can not be reached, and a heartbeat object which does not exist: the check says "unable to tell", and no host is reported as dead.
  • A pool whose heartbeat can not be started, and a pool whose delete fails (unit tests): the other pools keep their heartbeat, and the pool which is not deleted is monitored again.

Extend the existing KVM Host-HA heartbeat/VM-activity-check framework
(currently limited to NetworkFilesystem and SharedMountPoint pools) to
also cover RBD primary storage, based on the approach from the old
PR apache#5862, adapted to the current HA architecture and
reusing the multi-monitor Ceph support already added in apache#6792.

- Add StoragePoolType.RBD to LIBVIRT_STORAGE_POOL_TYPES_WITH_HA_SUPPORT,
  which is the single switch that makes pool registration, KVMHAMonitor,
  and the CheckOnHostCommand/CheckVMActivityOnStoragePoolCommand wrappers
  treat RBD pools as HA-capable.
- LibvirtStoragePool: build rbd/rados connection args (--mon-host, pool,
  and cephx --id/--key when set) from the pool's existing sourceHost/
  sourceDir/authUsername/authSecret fields for the heartbeat and
  VM-activity checks, mirroring the conventions KVMPhysicalDisk already
  uses to talk to RBD.
- Add kvmheartbeat_rbd.sh and kvmvmactivity_rbd.sh: RBD has no shared
  mount point to write a heartbeat file to, so the heartbeat timestamp
  is stored as a small RADOS object per host instead, and VM activity
  is detected via RBD watchers (rbd status) rather than file mtimes.
- Minor: fix a stale "NFS storage pool" log message in KVMHAMonitor now
  that this path also runs for RBD.
- Add LibvirtStoragePoolTest#testIsPoolSupportHA covering the new RBD
  case (no HA/heartbeat tests existed previously for any pool type).
@codecov

codecov Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 74.64115% with 53 lines in your changes missing coverage. Please review.
✅ Project coverage is 20.82%. Comparing base (b9b5436) to head (f0e5e68).
⚠️ Report is 41 commits behind head on main.

Files with missing lines Patch % Lines
...oud/hypervisor/kvm/storage/LibvirtStoragePool.java 74.03% 26 Missing and 1 partial ⚠️
...om/cloud/hypervisor/kvm/resource/KVMHAMonitor.java 50.00% 12 Missing ⚠️
...m/cloud/hypervisor/kvm/storage/KVMStoragePool.java 0.00% 9 Missing ⚠️
utils/src/main/java/com/cloud/utils/RbdUtils.java 89.28% 2 Missing and 1 partial ⚠️
...ervisor/kvm/resource/LibvirtComputingResource.java 0.00% 2 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main   #14320      +/-   ##
============================================
+ Coverage     20.75%   20.82%   +0.06%     
+ Complexity    21901    20924     -977     
============================================
  Files          6496     6054     -442     
  Lines        591602   548368   -43234     
  Branches      72583    73259     +676     
============================================
- Hits         122814   114194    -8620     
+ Misses       455456   420816   -34640     
- Partials      13332    13358      +26     
Flag Coverage Δ
uitests 3.86% <ø> (-0.02%) ⬇️
unittests 22.26% <74.64%> (+0.19%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@weizhouapache

Copy link
Copy Markdown
Member Author

@blueorangutan package

@blueorangutan

Copy link
Copy Markdown

@weizhouapache a [SL] Jenkins job has been kicked to build packages. It will be bundled with no SystemVM templates. I'll keep you posted as I make progress.

@blueorangutan

Copy link
Copy Markdown

Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 19426

@weizhouapache

Copy link
Copy Markdown
Member Author

@blueorangutan test

@blueorangutan

Copy link
Copy Markdown

@weizhouapache a [SL] Trillian-Jenkins test job (ol8 mgmt + kvm-ol8) has been kicked to run smoke tests

@weizhouapache weizhouapache added this to the 24.0 milestone Oct 6, 2026
@blueorangutan

Copy link
Copy Markdown

[SF] Trillian test result (tid-17079)
Environment: kvm-ol8 (x2), zone: Advanced Networking with Mgmt server ol8
Total time taken: 56468 seconds
Marvin logs: https://github.com/blueorangutan/acs-prs/releases/download/trillian/pr14320-t17079-kvm-ol8.zip
Smoke tests completed. 154 look OK, 2 have errors, 0 did not run
Only failed and skipped tests results shown below:

Test Result Time (s) Test File
test_01_vpn_usage Error 1.18 test_usage.py
ContextSuite context=TestCreateVolume>:setup Error 0.00 test_volumes.py
test_01_root_volume_encryption Error 1.62 test_volumes.py
test_02_data_volume_encryption Error 1.43 test_volumes.py
test_03_root_and_data_volume_encryption Error 1.43 test_volumes.py
test_02_attach_volume Error 209.22 test_volumes.py

@weizhouapache weizhouapache linked an issue Oct 7, 2026 that may be closed by this pull request
@weizhouapache
weizhouapache requested review from harikrishna-patnala and a lite review from Copilot October 7, 2026 14:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Critical RBD monitor-port and stale-heartbeat handling issues remain, along with incomplete HA cleanup.

3 open findings
What changed in this PR

Extends KVM Host HA heartbeat and VM-activity checks to Ceph RBD primary storage.

Changes:

  • Adds RBD HA support and connection handling.
  • Adds RADOS heartbeat and RBD watcher activity scripts.
  • Updates logging and adds HA eligibility tests.
File Summary
scripts/​vm/​hypervisor/​kvm/​kvmvmactivity_rbd.sh Adds RBD watcher-based VM activity detection.
scripts/​vm/​hypervisor/​kvm/​kvmheartbeat_rbd.sh Adds RADOS-backed heartbeat handling; stale delays over 255 seconds require bounded status handling.
plugins/​hypervisors/​kvm/​src/​test/​java/​com/​cloud/​hypervisor/​kvm/​storage/​LibvirtStoragePoolTest.java Tests RBD HA support eligibility.
plugins/​hypervisors/​kvm/​src/​main/​java/​com/​cloud/​hypervisor/​kvm/​storage/​LibvirtStoragePool.java Selects RBD scripts and builds Ceph arguments; monitor ports must be propagated, and missing-script errors should be generic.
plugins/​hypervisors/​kvm/​src/​main/​java/​com/​cloud/​hypervisor/​kvm/​resource/​KVMHAMonitor.java Generalizes the storage-pool log message.
engine/​components-api/​src/​main/​java/​com/​cloud/​ha/​HighAvailabilityManager.java Enables RBD HA support; corresponding pool deletion paths must also clean up HA state.

🧠 Review effort: Lite


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

- pass the pool's monitor port to the RBD heartbeat/activity scripts, for
  every monitor, instead of relying on the default port
- kvmheartbeat_rbd.sh: keep the heartbeat age out of the function return
  status, which wraps above 255 and made a long-stale heartbeat look ALIVE
- remove pools from the HA monitor on deletion for all storage pool types
  with HA support, without trying to umount an empty mount path
- make the missing-script error message generic
- add unit tests for the RBD monitor list (IPv4, IPv6, mixed, with/without port)
@weizhouapache
weizhouapache requested a lite review from Copilot October 8, 2026 09:19
@weizhouapache

Copy link
Copy Markdown
Member Author

@blueorangutan package

@blueorangutan

Copy link
Copy Markdown

@weizhouapache a [SL] Jenkins job has been kicked to build packages. It will be bundled with no SystemVM templates. I'll keep you posted as I make progress.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@blueorangutan

Copy link
Copy Markdown

Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 19467

…of the pool

Follow-up to the review of the Host HA on Ceph RBD support:

- kvmheartbeat_rbd.sh and kvmvmactivity_rbd.sh: exit with an error when the
  host IP, which names the RADOS objects, is missing (the self-fencing -c
  of the heartbeat script does not need it), or when a Ceph user is given
  without its key
- LibvirtStoragePool: trim the Ceph monitors and skip empty entries, with or
  without a port; pass the Ceph user and key only if both are set and fail
  with a clear error if only one of them is
- KVMHAMonitor: run umount without a shell when removing a pool
- kvmvmactivity_rbd.sh: fix the -t help text and drop the unused field from
  the stored activity state
- add unit tests for the monitor list (multiple IPv4 and IPv6 monitors, with
  and without a port, with spaces) and for the Ceph user and key handling
@weizhouapache

Copy link
Copy Markdown
Member Author

@blueorangutan package

@weizhouapache
weizhouapache requested a lite review from Copilot October 8, 2026 11:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Copilot review overview

6 open findings
11 resolved since last review

🧠 Review effort: Lite


Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.

…st HA checks, and harden the parsing

Follow-up to the review of the Host HA on Ceph RBD support:

- LibvirtStoragePool: do not add a port to Ceph monitors which have one
  already, and only wrap bare IPv6 addresses in square brackets; mask the
  Ceph key in the logged command
- kvmheartbeat_rbd.sh, kvmvmactivity_rbd.sh: give the Ceph key to rados and
  rbd in a temporary keyfile instead of --key
- kvmheartbeat_rbd.sh: require -t to be a number when checking a heartbeat
- kvmvmactivity_rbd.sh: read the list of volumes without word splitting or
  globbing, skipping empty entries, and check if the activity object exists
  by the exit status of rados stat
- add unit tests for monitors with a port and for the masked key
@blueorangutan

Copy link
Copy Markdown

@RosiKyu a [SL] Trillian-Jenkins test job (ol8 mgmt + kvm-ol8) has been kicked to run smoke tests

@blueorangutan

Copy link
Copy Markdown

[SF] Trillian test result (tid-211)
Environment: kvm-ubuntu26 (x2), zone: Advanced Networking with Mgmt server u26, storage: Ceph (x2)
Total time taken: 76348 seconds
Marvin logs: https://github.com/blueorangutan/acs-prs/releases/download/trillian/pr14320-n211-kvm-ubuntu26.zip
Smoke tests completed. 147 look OK, 10 have errors, 0 did not run
Only failed and skipped tests results shown below:

Test Result Time (s) Test File
test_01_non_strict_host_anti_affinity Failure 84.58 test_nonstrict_affinity_group.py
test_02_non_strict_host_affinity Error 37.20 test_nonstrict_affinity_group.py
test_01_add_primary_storage_disabled_host Error 38.17 test_primary_storage.py
test_01_primary_storage_scope_change Error 0.14 test_primary_storage_scope.py
test_01_sys_vm_start Failure 0.17 test_secondary_storage.py
ContextSuite context=TestCpuCapServiceOfferings>:setup Error 0.00 test_service_offerings.py
test_01_deploy_vm_on_specific_host Error 0.14 test_vm_deployment_planner.py
test_04_deploy_vm_on_host_override_pod_and_cluster Error 0.19 test_vm_deployment_planner.py
test_01_migrate_VM_and_root_volume Error 73.38 test_vm_life_cycle.py
test_02_migrate_VM_with_two_data_disks Error 25.82 test_vm_life_cycle.py
ContextSuite context=TestDeployVMFromSnapshotOrVolume>:setup Error 0.00 test_vm_lifecycle_with_snapshot_or_volume.py
test_01_create_vm_snapshots Error 227.64 test_vm_snapshots.py
test_02_revert_vm_snapshots Failure 90.32 test_vm_snapshots.py
test_03_delete_vm_snapshots Failure 0.03 test_vm_snapshots.py
test_01_root_volume_encryption Failure 145.73 test_volumes.py
test_02_data_volume_encryption Failure 316.60 test_volumes.py
test_03_root_and_data_volume_encryption Failure 98.74 test_volumes.py

@blueorangutan

Copy link
Copy Markdown

[SF] Trillian test result (tid-17555)
Environment: kvm-ol8 (x2), zone: Advanced Networking with Mgmt server ol8
Total time taken: 71798 seconds
Marvin logs: https://github.com/blueorangutan/acs-prs/releases/download/trillian/pr14320-t17555-kvm-ol8.zip
Smoke tests completed. 157 look OK, 0 have errors, 0 did not run
Only failed and skipped tests results shown below:

Test Result Time (s) Test File

@weizhouapache
weizhouapache requested a review from wido November 12, 2026 19:47
@weizhouapache
weizhouapache marked this pull request as ready for review November 13, 2026 10:04
Copilot AI lite review requested due to automatic review settings November 13, 2026 10:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread scripts/vm/hypervisor/kvm/kvmha_rbd.py Resolved
A pool is removed from the HA monitor before it is deleted, so that no
heartbeat is written while the pool is removed: the monitor holds its
lock while it writes the heartbeats, so none is on going when the pool
is removed from it, and a write which fails while the pool is unmounted
could fence the host.

If the delete does not succeed or throws, the pool is still there, but
it was no longer monitored, so the host stopped writing its heartbeat on
a pool which its VMs may still use. It is added to the monitor again.
addRbdConnectionArgs requires the Ceph user and its key to be either
both set or both unset. The key in the environment of the scripts is
now given under the same condition, and not whenever it is set.
If the cluster could not be connected, or the pool could not be opened,
the error was dropped, and the operations failed with the name of an
object, so that the output of the scripts did not say what was wrong.
The reason of librados is now kept, and the scripts print it when a
heartbeat can not be read or written, or when the activity of the VMs
can not be checked. The exit status does not change: a failed read says
that it is not known whether the host is alive (2), a failed write of
the heartbeat is a failure (1).

kvmvmactivity_rbd.py exits with 2 for an argument which is not valid, as
it does for the other arguments and as its header says.

The heartbeat is written as an integer timestamp explicitly.
Copilot AI lite review requested due to automatic review settings November 13, 2026 12:37
@weizhouapache

Copy link
Copy Markdown
Member Author

Thanks for the review. I pushed three commits that address it: 392ce05, 4398ac1 and 183c5a9. One comment at a time:

kvmheartbeat_rbd.py: %d with time.time(). This does not raise in Python 3. '%d' % 1794569667.3 gives '1794569667', and only '{:d}'.format(float) raises. The heartbeat has been written this way on real hosts through all the tests. I made the conversion explicit with int(time.time()) anyway (183c5a9).

KVMStoragePoolManager: unregistering before the delete (two comments). The order is deliberate and is the one on main. The monitor holds its lock for a whole heartbeat cycle, so once the pool is unregistered no heartbeat is running and none is written while the pool is unmounted or removed. A write that fails at that point could fence the host. The concern about a failed delete is valid, though: the pool stays but is no longer monitored. If the delete does not succeed or throws, the pool is now registered with the monitor again (392ce05). The unit tests cover a failed delete, an exception, a successful delete and a pool that was not monitored.

kvmha_rbd.py: the connection error is swallowed. I kept the connection from raising in the constructor on purpose. The scripts need a failed connection to be a failed operation with the exit status they already have: a read says it is not known whether the host is alive (2), and a failed heartbeat write is a failure (1). What was missing was the reason. It is now kept and included in the CephError and WatchersError, and the scripts print it. For example: Unable to read the RADOS object [KVMHA-hb-...] in pool [pool]: unable to connect to the cluster or to open the pool [pool]: <error of librados> (183c5a9).

kvmvmactivity_rbd.py: exit status for invalid arguments. Fixed. The argument parser now exits with 2, as the header says and as the other argument checks do (183c5a9).

LibvirtStoragePool: CEPH_KEY without a user. Fixed. The key is passed to the scripts only together with its user, as addRbdConnectionArgs requires, and there is a test for it (4398ac1).

Older thread on kvmvmactivity_rbd.sh ("DEAD reason is misleading"). That .sh script no longer exists in this PR. The Python script gives the reason of the branch that is taken: in the else case the message says suspectTimeDiff -lt 0 was not satisfied and latestUpdateTime -gt lastUpdateTime was not either. Nothing to change, so the thread can be resolved.

I also ran the same changes (on the 4.23 branch) in a nested environment with KVM hosts and Ceph RBD primary storage. The 9 tests in test_hostha_kvm.py pass. When I force-stopped a KVM host, it was marked Down after about 3.5 minutes and its two VMs restarted on the other host.

@weizhouapache

Copy link
Copy Markdown
Member Author

@blueorangutan package

@blueorangutan

Copy link
Copy Markdown

@weizhouapache a [SL] Jenkins job has been kicked to build packages. It will be bundled with no SystemVM templates. I'll keep you posted as I make progress.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Copilot review overview

6 open issues
6 resolved since last review

Review effort: Lite

Comment on lines 492 to 493
synchronized (_storagePools) {
_storagePools.remove(uuid);
Comment on lines +447 to +455
volumeListFile = File.createTempFile("kvmha-volumes-", ".txt");
Files.write(volumeListFile.toPath(), volumes.getBytes(StandardCharsets.UTF_8));
cmd.add("-U", volumeListFile.getAbsolutePath());
return volumeListFile;
} catch (IOException e) {
logger.warn("Unable to write the list of volumes to a file, giving it to the script as an argument: {}", e.getMessage());
if (volumeListFile != null && !volumeListFile.delete()) {
logger.debug("Unable to delete the file {}", volumeListFile);
}

@bhouse-nexthop bhouse-nexthop left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @weizhouapache, both follow-ups look good to me.

  • 7. RBD and SharedMountPoint pools destroyed on removal: resolved in 9e50a47. Only the HA unregister depends on the HA type list now. NFS keeps the uuid-only delete, which unmounts it, and every other type, RBD and SharedMountPoint included, goes back to the delete that takes the details, as on main. KVMStoragePoolManagerTest now splits NFS from RBD/SharedMountPoint and verifies unregisterStoragePool, which matches the code.
  • Heartbeat age slack: resolved in b5a699d. The readers get heartbeat.update.timeout through -w, which is the same timeout the agent uses to kill a write in createHeartBeatCommand. The fallback is 2 * TIMEOUT, which covers connect plus write. The new LibvirtStoragePoolTest cases check that -w goes only to the RBD script.

On the new Copilot comments:

  • "%d" % time.time() is fine in Python 3, as you showed.
  • The Parser.error() exit status of 1 in kvmvmactivity_rbd.py doesn't match the header's "2 if the arguments are not valid". It makes no difference in practice, since hasVmActivity treats any non-zero exit as "not dead". It's a one-line fix if you want the header and the code to agree.
  • Unregistering before the delete has the same ordering as main had for NFS, so I don't consider it a regression for this PR.

One small note, not blocking. runHeartBeat() writes the pools one after another before it sleeps. So on a host with several RBD pools on the same slow cluster, the gap between two heartbeats of one pool can be the interval plus one slow write per pool, not just one. With the any/all rule and the default of a single pool per cluster this is an edge case. A comment next to heartbeat_max_age would be enough.

Please also refresh the PR description, which still describes kvmheartbeat_rbd.sh / kvmvmactivity_rbd.sh, rbd status and --id/--key on the command line. It should cover the Python/librados scripts, the key in CEPH_KEY, the GUID-named objects, Suggests + --check-bindings, and the fact that VM HA relies on the heartbeat only for RBD-only clusters without OOBM.

LGTM.

A pool of a type with HA support (a libvirt storage pool) which is not
deleted is monitored for HA again, as it is still there, but it was
still removed from the information which the manager keeps to create the
pool again. It now stays known as well.

The other types keep being forgotten when their delete does not
succeed: their adaptors return false if they have no such pool.

The tests of the delete are reduced to the cases which differ.
The agent writes the heartbeats of its pools one after another, so with
several RBD pools on the same slow cluster the gap between two
heartbeats of a pool can be longer than the margin, which covers one
slow write. It is an edge case, which is now said next to the margin.
Copilot AI lite review requested due to automatic review settings November 13, 2026 13:22
@weizhouapache

Copy link
Copy Markdown
Member Author

Thanks @weizhouapache, both follow-ups look good to me.

  • 7. RBD and SharedMountPoint pools destroyed on removal: resolved in 9e50a47. Only the HA unregister depends on the HA type list now. NFS keeps the uuid-only delete, which unmounts it, and every other type, RBD and SharedMountPoint included, goes back to the delete that takes the details, as on main. KVMStoragePoolManagerTest now splits NFS from RBD/SharedMountPoint and verifies unregisterStoragePool, which matches the code.
  • Heartbeat age slack: resolved in b5a699d. The readers get heartbeat.update.timeout through -w, which is the same timeout the agent uses to kill a write in createHeartBeatCommand. The fallback is 2 * TIMEOUT, which covers connect plus write. The new LibvirtStoragePoolTest cases check that -w goes only to the RBD script.

On the new Copilot comments:

  • "%d" % time.time() is fine in Python 3, as you showed.
  • The Parser.error() exit status of 1 in kvmvmactivity_rbd.py doesn't match the header's "2 if the arguments are not valid". It makes no difference in practice, since hasVmActivity treats any non-zero exit as "not dead". It's a one-line fix if you want the header and the code to agree.
  • Unregistering before the delete has the same ordering as main had for NFS, so I don't consider it a regression for this PR.

One small note, not blocking. runHeartBeat() writes the pools one after another before it sleeps. So on a host with several RBD pools on the same slow cluster, the gap between two heartbeats of one pool can be the interval plus one slow write per pool, not just one. With the any/all rule and the default of a single pool per cluster this is an edge case. A comment next to heartbeat_max_age would be enough.

Please also refresh the PR description, which still describes kvmheartbeat_rbd.sh / kvmvmactivity_rbd.sh, rbd status and --id/--key on the command line. It should cover the Python/librados scripts, the key in CEPH_KEY, the GUID-named objects, Suggests + --check-bindings, and the fact that VM HA relies on the heartbeat only for RBD-only clusters without OOBM.

LGTM.

thanks @bhouse-nexthop for review

updated as suggested

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@blueorangutan

Copy link
Copy Markdown

Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 20049

@blueorangutan

Copy link
Copy Markdown

[SF] Trillian test result (tid-219)
Environment: kvm-ubuntu26 (x2), zone: Advanced Networking with Mgmt server u26, storage: Ceph (x2)
Total time taken: 78013 seconds
Marvin logs: https://github.com/blueorangutan/acs-prs/releases/download/trillian/pr14320-n219-kvm-ubuntu26.zip
Smoke tests completed. 153 look OK, 4 have errors, 0 did not run
Only failed and skipped tests results shown below:

Test Result Time (s) Test File
test_uservm_host_control_state Failure 18.47 test_host_control_state.py
test_01_migrate_VM_and_root_volume Error 95.12 test_vm_life_cycle.py
test_02_migrate_VM_with_two_data_disks Error 24.71 test_vm_life_cycle.py
test_04_nonsecured_to_secured_vm_migration Error 3882.46 test_vm_life_cycle.py
test_04_nonsecured_to_secured_vm_migration Error 3882.71 test_vm_life_cycle.py
test_08_migrate_vm Error 20.80 test_vm_life_cycle.py
test_09_expunge_vm Failure 425.89 test_vm_life_cycle.py
test_13_destroy_and_expunge_vm Error 10.50 test_vm_life_cycle.py
test_01_create_vm_snapshots Error 158.60 test_vm_snapshots.py
test_02_revert_vm_snapshots Failure 90.28 test_vm_snapshots.py
test_03_delete_vm_snapshots Failure 0.03 test_vm_snapshots.py
test_01_root_volume_encryption Failure 115.62 test_volumes.py
test_02_data_volume_encryption Failure 238.66 test_volumes.py
test_03_root_and_data_volume_encryption Failure 51.57 test_volumes.py

bhouse-nexthop pushed a commit to bhouse-nexthop/cloudstack that referenced this pull request Nov 13, 2026
Extend the KVM HA heartbeat and VM-activity check, until now limited to
NetworkFilesystem and SharedMountPoint pools, to Ceph RBD primary
storage. Each host writes its heartbeat to a RADOS object in the pool
named after its GUID, through Python scripts which use the librados and
librbd bindings (python3-rados and python3-rbd, now suggested by the
agent packages). A pool whose heartbeat can not be used is not
monitored for HA, and a pool which is not deleted is monitored again.

Backport of apache#14320 (head f0e5e68)

Conflict resolutions:
- KVMStoragePoolManagerTest: both sides add the class. Merge the tests
  of upstream into our existing class (getPhysicalDisk retries and pool
  lock tests), sharing one setUp.

Signed-off-by: Brad House <[email protected]>
bhouse-nexthop pushed a commit to bhouse-nexthop/cloudstack that referenced this pull request Nov 13, 2026
Extend the KVM HA heartbeat and VM-activity check, until now limited to
NetworkFilesystem and SharedMountPoint pools, to Ceph RBD primary
storage. Each host writes its heartbeat to a RADOS object in the pool
named after its GUID, through Python scripts which use the librados and
librbd bindings (python3-rados and python3-rbd, now suggested by the
agent packages). A pool whose heartbeat can not be used is not
monitored for HA, and a pool which is not deleted is monitored again.

Backport of apache#14320 (head f0e5e68)

Conflict resolutions on 4.22:
- KVMHAMonitor: add unregisterStoragePool(), which main got from apache#13897
  and the pool deletion of this change needs (it drops the pool from
  the monitor without the umount of removeStoragePool()).
- KVMStoragePoolManager: take the deletion of upstream as is, so the
  delete with details uses the delete without details for
  NetworkFilesystem, as on main, and the details one (which does not
  destroy the libvirt pool or secret) for RBD and the other types. Keep
  our unsynchronized deleteStoragePool(type, uuid) of the per-pool locks.
- KVMStoragePoolManagerTest: merge the tests of upstream into our
  existing class (getPhysicalDisk retries and pool lock tests), sharing
  one setUp.
- LibvirtStoragePoolTest: drop the resolvePhysicalDiskFile tests which
  came along from main and test code 4.22 does not have.
- LibvirtComputingResourceTest: take the new testCheckOnHostCommand
  assertions and import CheckOnHostAnswer.
- el8 spec: add only the Suggests, not the Requires of main.

Signed-off-by: Brad House <[email protected]>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Status] KVM Host-HA using CEPH RBD

7 participants