task/ansible: mark nodes down on hardware failures - #2236
Conversation
|
The feature looks interesting, though I haven't read deeply what the criteria of marking host down. |
|
Also if we want this mechanism incorporated it is better to put the documentation into teuthology how all the stuff supposed to work, and that it is relied on ansible task or/and on ceph-cm-ansible role task and what format is expected to trigger the node marking down, etc. |
|
Thanks — both points addressed, force-pushed. No paddles calls from the task. The The supervisor also only marks down nodes that are actually targets of the job it just ran, and only the Config option. Docs. New To your first line, the criteria: exactly one pattern today, New tests in |
| hardware_failure_patterns = [ | ||
| # ceph-cm-ansible's "Ensure we found enough OSD disks" task; a disk is | ||
| # missing or dead | ||
| r"Wanted \d+ disks? of .+ but only matched \d+", | ||
| ] |
There was a problem hiding this comment.
I wonder if it would be better to move patterns to config based on machine types, so instead of
mark_down_on_hardware_failure: true
We can have:
disable_targets:
ansible_failure_patterns:
trial:
- "Wanted \d+ disks? of .+ but only matched \d+"
or this is over engineered?
There was a problem hiding this comment.
Well, in your example, both the smithi and trials have that check. gibba would if we were using them too. So we'd have to duplicate the entries.
There was a problem hiding this comment.
In this case yaml allows to copy data using anchors:
disable_targets:
ansible_failure_patterns:
trial: &missing_or_dead
- "Wanted \d+ disks? of .+ but only matched \d+"
gibba: *missing_or_dead
smithi: *missing_or_dead
There was a problem hiding this comment.
TIL about yaml anchors
| else: | ||
| log.info('Success!') | ||
| if 'targets' in job_config: | ||
| check_for_hardware_failures_and_mark_down(job_config) |
There was a problem hiding this comment.
I think it is worth to add swift comment that we should try to make the targets down always before unlocking.
There was a problem hiding this comment.
I'm not sure what you mean. A comment in the actual code or a log?
There was a problem hiding this comment.
I meant in the code, so if the one refactor code take this into account.
|
Hey David, |
https://pulpito.ceph.com/dgalloway-2026-08-10_21:48:40-smoke-main-distro-default-trial/453342/ Yes, also added second commit to change the description.
|
We already mark a node down after it fails to reimage 10 times in a row. When ceph-cm-ansible's "Ensure we found enough OSD disks" task fails, though, we know right away that the node is missing a disk, and every job that lands on it until someone notices will fail the same way. Have the supervisor read back the ansible failure log the job archived, and mark down any node whose failure matches a pattern configured for that machine type in disable_targets.ansible_failure_patterns. The job process only archives the log as it always has; node manipulation stays on the supervisor side so a running job never depends on the lock server being reachable. Nodes are marked down before they're unlocked so nothing else can grab one in the meantime, and the description is set afterwards, since unlock_targets() matches on it. See docs/node_health.rst. Fixes: https://tracker.ceph.com/issues/75669 Signed-off-by: David Galloway <david.galloway@ibm.com>

Fixes: https://tracker.ceph.com/issues/75669
Related: https://tracker.ceph.com/issues/75208
What
We already mark a node down after it fails to reimage 10 times in a row. When ceph-cm-ansible's
Ensure we found enough OSD diskstask fails, though, we know right away that the node is missing a disk — and every job that lands on it until someone notices will fail the same way.This scans the ansible failure log for messages that mean the hardware itself is broken and marks the affected nodes down immediately.
FailureAnalyzer.find_hardware_failures()parses the failure log (keyed by hostname) and returns{hostname: msg}for failures matching a known hardware-failure pattern. The one pattern for now isWanted N disks of ... but only matched M. Whitespace is collapsed before matching because ansible wraps that message across lines.Ansible._handle_failure()feeds the hits to a new_mark_hardware_failures_down()hook — a no-op in the base class, implemented inCephLabaslock_ops.update_lock(hostname, status='down'). This mirrors the existing_set_status()split. It's wrapped in its own try/except so a lock server problem can't disturb the existing failure-reporting path.Notes for reviewers
Two deliberate choices:
The lock description is left untouched. The dispatcher's 10-strikes path sets
description='reimage failed 10 times', but it runs after unlocking. Here the job still holds the lock, andunlock_targets()refuses to unlock a node whose description no longer matchesarchive_path— so setting it would leave the dead node locked forever. Onlyupis flipped; the reason goes to the log.We match on the message, not the task name. ceph-cm-ansible's
callback_plugins/failure_log.pyonly records the result dict (msg,changed, ...), never the task name. Verified against a real ansible_failures.yaml from the tracker and againstroles/testnode/tasks/configure_lvm.yml. Keying off the task name instead would require a callback plugin change in ceph-cm-ansible first.Testing
8 new cases in
tests/task/test_ansible.py: analyzer coverage (the verbatim message from job 116107, a line-wrapped variant, aresultslist, a non-matching failure, an empty log) plus end-to-end checks thatCephLabmarks the node down,Ansibledoes not, and a non-hardware failure does not.tests/task/test_ansible.py: 105 passed, 6 skipped. Full suite: 1222 passed, 3 failed — the 3 are intests/orchestra/test_connection.pyand fail identically on a clean checkout of main.