Repository navigation
Conversation
AnsibleHelper.run() can see ansible-playbook exit during its 0.1s sleep, before the callback file's last RESULT line is read. That RESULT replaces _cb_results, so a return code stored earlier is lost and Closing up raises KeyError: 'ansible-playbook_rc'. Store the return code after the leftover callback lines are consumed. Signed-off-by: Jitin Pillai <Jitin.Pillai@stratus.com>
Signed-off-by: Jitin Pillai <Jitin.Pillai@stratus.com>
peter-boden
left a comment
There was a problem hiding this comment.
A few comments + I would put the fix and the accompanying tests in the same commit.
| buffer += output | ||
| if buffer[-1] == '\n': | ||
| self._process_output(buffer) | ||
| buffer = '' | ||
| if buffer: | ||
| self._process_output(buffer) |
There was a problem hiding this comment.
This adds partial-line buffering, a half-read line from the main loop is now joined with the rest instead of being parsed as two broken fragments?
It isn't needed for the rc fix, it's not mentioned in the commit message, and it has no test. I would either revert and just move the rc assignment, or add a test(s) for it and mention it in the message.
| def test_result_already_read_keeps_return_code(monkeypatch): | ||
| result = _run(monkeypatch, 'early') | ||
| assert result['ansible-playbook_rc'] == 0 | ||
| assert result['otopi_localvm_dir']['path'] == ( | ||
| '/var/tmp/localvmtest' | ||
| ) |
There was a problem hiding this comment.
This test also passes on the pre-fix code, so it doesn't really test anything. The RESULT is written before the process starts, and the main loop reads it before poll() reports the exit. The drain loop never sees it. Only test_result_written_during_sleep_keeps_return_code fails without the fix (I checked: KeyError: 'ansible-playbook_rc'). Please drop this test, or change it so the RESULT is written after the last poll, so it exercises the drain.
Thanks. I'll put the fix and the test in one commit, keep the existing drain and only move the ansible-playbook_rc assignment to after it, and drop test_result_already_read_keeps_return_code. The late RESULT test is the one that fails on the old code. |
Summary
AnsibleHelper.run() reads the otopi callback file, sleeps 0.1s, and then checks whether ansible-playbook has exited. If the process exits during that sleep, its last callback line is still unread. That line is the RESULT object from v2_playbook_on_stats, and _process_output() replaces _cb_results with it.
The return code was stored before that drain, so RESULT removed ansible-playbook_rc. misc.py Closing up then raised KeyError, and deploy cleanup deleted the engine VM even though the playbook exited 0.
Store the return code after the leftover callback lines are consumed.
Reproduction
Seen twice during hosted-engine --deploy with the packaged ovirt-hosted-engine-setup-2.7.2-1.el9.noarch on Oracle Linux 9.8, kernel 5.14.0-687.5.3.el9_8. rpm -V showed the installed ansible_utils.py, callback plugin, and misc.py were unmodified. The same sequence is in current master.
It is timing-dependent. One deploy ran four playbooks. Three kept the return code. One lost it:
An earlier deploy showed the same pair one millisecond apart: "ansible-playbook rc: 0", then KeyError in Closing up. The result dictionary still had the playbook facts (otopi_localvm_dir, appliance size, admin username) and no ansible-playbook_rc. The same KeyError was reported on the users list in April 2023: https://lists.ovirt.org/archives/list/users@ovirt.org/thread/QO2Z6OJTERAJCCLYHC6CLRQKVUUJRG2C/
Log lines to look for, in order:
Testing
tests/ansible_utils_test.py drives AnsibleHelper.run() with a fake playbook.
hosted-engine --deploy was then run to completion multiple times with this ordering. Each run finished and the engine VM stayed in place. Those runs show a normal deploy still completes. The late-RESULT unit test is what forces the 0.1s window, which a successful deploy can miss.