Add stats on rescued/ignored tasks (#48418)

* Adding rescued/ignored tasks to stats gathering

Fixes #31245

* Amend integration tests to pass

* callback/dense.py: fix too-many-format-args

* Add changelog

* Amend counter_enabled and unixy callbacks

* Fix syntax error

* Fix typo in the changelog

* Remove not needed comment

* Re-add skipped

* Add test for rescued

* Fix colors...

* Fix unstable tests?

* Add a note to the porting guide

* Re-word the note in the porting guide

Fixes #20346
Fixes #24525
Fixes #14393

Co-authored-by: James Cammarata <jimi@sngx.net>
Co-authored-by: Martin Krizek <martin.krizek@gmail.com>
This commit is contained in:
Martin Krizek 2019-02-20 04:00:47 +01:00 committed by Jordan Borean
parent b1a9e7b8c8
commit be9f07279e
17 changed files with 107 additions and 41 deletions

View file

@ -0,0 +1,2 @@
minor_changes:
- Add stats on rescued/ignored tasks to play recap (https://github.com/ansible/ansible/pull/48418)

View file

@ -230,6 +230,7 @@ Plugins
``CLIARGS.get('tags')`` and ``CLIARGS['tags']`` work as expected but you won't be able to modify
the cli arguments at all.
* Play recap now counts ``ignored`` and ``rescued`` tasks as well as ``ok``, ``changed``, ``unreachable``, ``failed`` and ``skipped`` tasks, thanks to two additional stat counters in the ``default`` callback plugin. Tasks that fail and have ``ignore_errors: yes`` set are listed as ``ignored``. Tasks that fail and then execute a rescue section are listed as ``rescued``. Note that ``rescued`` tasks are no longer counted as ``failed`` as in Ansible 2.7 (and earlier).
Porting custom scripts
======================

View file

@ -34,6 +34,8 @@ class AggregateStats:
self.dark = {}
self.changed = {}
self.skipped = {}
self.rescued = {}
self.ignored = {}
# user defined stats, which can be per host or global
self.custom = {}
@ -63,7 +65,9 @@ class AggregateStats:
failures=self.failures.get(host, 0),
unreachable=self.dark.get(host, 0),
changed=self.changed.get(host, 0),
skipped=self.skipped.get(host, 0)
skipped=self.skipped.get(host, 0),
rescued=self.rescued.get(host, 0),
ignored=self.ignored.get(host, 0),
)
def set_custom_stats(self, which, what, host=None):

View file

@ -85,21 +85,25 @@ class CallbackModule(CallbackBase):
for host in hosts:
stat = stats.summarize(host)
self._display.display(u"%s : %s %s %s %s" % (
self._display.display(u"%s : %s %s %s %s %s %s" % (
hostcolor(host, stat),
colorize(u'ok', stat['ok'], C.COLOR_OK),
colorize(u'changed', stat['changed'], C.COLOR_CHANGED),
colorize(u'unreachable', stat['unreachable'], C.COLOR_UNREACHABLE),
colorize(u'failed', stat['failures'], C.COLOR_ERROR)),
colorize(u'failed', stat['failures'], C.COLOR_ERROR),
colorize(u'rescued', stat['rescued'], C.COLOR_OK),
colorize(u'ignored', stat['ignored'], C.COLOR_WARN)),
screen_only=True
)
self._display.display(u"%s : %s %s %s %s" % (
self._display.display(u"%s : %s %s %s %s %s %s" % (
hostcolor(host, stat, False),
colorize(u'ok', stat['ok'], None),
colorize(u'changed', stat['changed'], None),
colorize(u'unreachable', stat['unreachable'], None),
colorize(u'failed', stat['failures'], None)),
colorize(u'failed', stat['failures'], None),
colorize(u'rescued', stat['rescued'], None),
colorize(u'ignored', stat['ignored'], None)),
log_only=True
)

View file

@ -327,23 +327,31 @@ class CallbackModule(CallbackBase):
for h in hosts:
t = stats.summarize(h)
self._display.display(u"%s : %s %s %s %s %s" % (
hostcolor(h, t),
colorize(u'ok', t['ok'], C.COLOR_OK),
colorize(u'changed', t['changed'], C.COLOR_CHANGED),
colorize(u'unreachable', t['unreachable'], C.COLOR_UNREACHABLE),
colorize(u'failed', t['failures'], C.COLOR_ERROR),
colorize(u'skipped', t['skipped'], C.COLOR_SKIP)),
self._display.display(
u"%s : %s %s %s %s %s %s %s" % (
hostcolor(h, t),
colorize(u'ok', t['ok'], C.COLOR_OK),
colorize(u'changed', t['changed'], C.COLOR_CHANGED),
colorize(u'unreachable', t['unreachable'], C.COLOR_UNREACHABLE),
colorize(u'failed', t['failures'], C.COLOR_ERROR),
colorize(u'skipped', t['skipped'], C.COLOR_SKIP),
colorize(u'rescued', t['rescued'], C.COLOR_OK),
colorize(u'ignored', t['ignored'], C.COLOR_WARN),
),
screen_only=True
)
self._display.display(u"%s : %s %s %s %s %s" % (
hostcolor(h, t, False),
colorize(u'ok', t['ok'], None),
colorize(u'changed', t['changed'], None),
colorize(u'unreachable', t['unreachable'], None),
colorize(u'failed', t['failures'], None),
colorize(u'skipped', t['skipped'], None)),
self._display.display(
u"%s : %s %s %s %s %s %s %s" % (
hostcolor(h, t, False),
colorize(u'ok', t['ok'], None),
colorize(u'changed', t['changed'], None),
colorize(u'unreachable', t['unreachable'], None),
colorize(u'failed', t['failures'], None),
colorize(u'skipped', t['skipped'], None),
colorize(u'rescued', t['rescued'], None),
colorize(u'ignored', t['ignored'], None),
),
log_only=True
)

View file

@ -481,12 +481,16 @@ class CallbackModule_dense(CallbackModule_default):
hosts = sorted(stats.processed.keys())
for h in hosts:
t = stats.summarize(h)
self._display.display(u"%s : %s %s %s %s" % (
hostcolor(h, t),
colorize(u'ok', t['ok'], C.COLOR_OK),
colorize(u'changed', t['changed'], C.COLOR_CHANGED),
colorize(u'unreachable', t['unreachable'], C.COLOR_UNREACHABLE),
colorize(u'failed', t['failures'], C.COLOR_ERROR)),
self._display.display(
u"%s : %s %s %s %s %s %s" % (
hostcolor(h, t),
colorize(u'ok', t['ok'], C.COLOR_OK),
colorize(u'changed', t['changed'], C.COLOR_CHANGED),
colorize(u'unreachable', t['unreachable'], C.COLOR_UNREACHABLE),
colorize(u'failed', t['failures'], C.COLOR_ERROR),
colorize(u'rescued', t['rescued'], C.COLOR_OK),
colorize(u'ignored', t['ignored'], C.COLOR_WARN),
),
screen_only=True
)

View file

@ -237,8 +237,8 @@ class CallbackModule(CallbackBase):
else:
color = 'ok'
msg = '{0} : ok={1}\tchanged={2}\tfailed={3}\tunreachable={4}'.format(
host, s['ok'], s['changed'], s['failures'], s['unreachable'])
msg = '{0} : ok={1}\tchanged={2}\tfailed={3}\tunreachable={4}\trescued={5}\tignored={6}'.format(
host, s['ok'], s['changed'], s['failures'], s['unreachable'], s['rescued'], s['ignored'])
print(colorize(msg, color))
def v2_runner_on_skipped(self, result, **kwargs):

View file

@ -207,7 +207,7 @@ class CallbackModule(CallbackBase):
hosts = sorted(stats.processed.keys())
t = prettytable.PrettyTable(['Host', 'Ok', 'Changed', 'Unreachable',
'Failures'])
'Failures', 'Rescued', 'Ignored'])
failures = False
unreachable = False
@ -221,7 +221,7 @@ class CallbackModule(CallbackBase):
unreachable = True
t.add_row([h] + [s[k] for k in ['ok', 'changed', 'unreachable',
'failures']])
'failures', 'rescued', 'ignored']])
attachments = []
msg_items = [

View file

@ -173,21 +173,25 @@ class CallbackModule(CallbackBase):
# TODO how else can we display these?
t = stats.summarize(h)
self._display.display(u" %s : %s %s %s %s" % (
self._display.display(u" %s : %s %s %s %s %s %s" % (
hostcolor(h, t),
colorize(u'ok', t['ok'], C.COLOR_OK),
colorize(u'changed', t['changed'], C.COLOR_CHANGED),
colorize(u'unreachable', t['unreachable'], C.COLOR_UNREACHABLE),
colorize(u'failed', t['failures'], C.COLOR_ERROR)),
colorize(u'failed', t['failures'], C.COLOR_ERROR),
colorize(u'rescued', t['rescued'], C.COLOR_OK),
colorize(u'ignored', t['ignored'], C.COLOR_WARN)),
screen_only=True
)
self._display.display(u" %s : %s %s %s %s" % (
self._display.display(u" %s : %s %s %s %s %s %s" % (
hostcolor(h, t, False),
colorize(u'ok', t['ok'], None),
colorize(u'changed', t['changed'], None),
colorize(u'unreachable', t['unreachable'], None),
colorize(u'failed', t['failures'], None)),
colorize(u'failed', t['failures'], None),
colorize(u'rescued', t['rescued'], None),
colorize(u'ignored', t['ignored'], None)),
log_only=True
)

View file

@ -458,9 +458,6 @@ class StrategyBase:
else:
iterator.mark_host_failed(original_host)
# increment the failed count for this host
self._tqm._stats.increment('failures', original_host.name)
# grab the current state and if we're iterating on the rescue portion
# of a block then we save the failed task in a special var for use
# within the rescue/always
@ -470,6 +467,7 @@ class StrategyBase:
self._tqm._failed_hosts[original_host.name] = True
if state and iterator.get_active_state(state).run_state == iterator.ITERATING_RESCUE:
self._tqm._stats.increment('rescued', original_host.name)
self._variable_manager.set_nonpersistent_facts(
original_host,
dict(
@ -477,8 +475,11 @@ class StrategyBase:
ansible_failed_result=task_result._result,
),
)
else:
self._tqm._stats.increment('failures', original_host.name)
else:
self._tqm._stats.increment('ok', original_host.name)
self._tqm._stats.increment('ignored', original_host.name)
if 'changed' in task_result._result and task_result._result['changed']:
self._tqm._stats.increment('changed', original_host.name)
self._tqm.send_callback('v2_runner_on_failed', task_result, ignore_errors=ignore_errors)

View file

@ -22,6 +22,12 @@ changed: [testhost] => (item=foo-1)
changed: [testhost] => (item=foo-2)
changed: [testhost] => (item=foo-3)
TASK [EXPECTED FAILURE Failed task to be rescued] ******************************
fatal: [testhost]: FAILED! => {"changed": false, "msg": "Failed as requested from task"}
TASK [Rescue task] *************************************************************
changed: [testhost]
RUNNING HANDLER [Test handler 1] ***********************************************
changed: [testhost]
@ -40,5 +46,5 @@ TASK [Second free task] ********************************************************
changed: [testhost]
PLAY RECAP *********************************************************************
testhost : ok=10 changed=7 unreachable=0 failed=0 skipped=1
testhost : ok=11 changed=8 unreachable=0 failed=0 skipped=1 rescued=1 ignored=1

View file

@ -1,3 +1,4 @@
+ ansible-playbook -i inventory test.yml
++ set +x
fatal: [testhost]: FAILED! => {"changed": false, "msg": "no reason"}
fatal: [testhost]: FAILED! => {"changed": false, "msg": "Failed as requested from task"}

View file

@ -21,6 +21,11 @@ changed: [testhost] => (item=foo-1)
changed: [testhost] => (item=foo-2)
changed: [testhost] => (item=foo-3)
TASK [EXPECTED FAILURE Failed task to be rescued] ******************************
TASK [Rescue task] *************************************************************
changed: [testhost]
RUNNING HANDLER [Test handler 1] ***********************************************
changed: [testhost]
@ -39,5 +44,5 @@ TASK [Second free task] ********************************************************
changed: [testhost]
PLAY RECAP *********************************************************************
testhost : ok=10 changed=7 unreachable=0 failed=0 skipped=1
testhost : ok=11 changed=8 unreachable=0 failed=0 skipped=1 rescued=1 ignored=1

View file

@ -19,6 +19,12 @@ changed: [testhost] => (item=foo-1)
changed: [testhost] => (item=foo-2)
changed: [testhost] => (item=foo-3)
TASK [EXPECTED FAILURE Failed task to be rescued] ******************************
fatal: [testhost]: FAILED! => {"changed": false, "msg": "Failed as requested from task"}
TASK [Rescue task] *************************************************************
changed: [testhost]
RUNNING HANDLER [Test handler 1] ***********************************************
changed: [testhost]
@ -34,5 +40,5 @@ TASK [Second free task] ********************************************************
changed: [testhost]
PLAY RECAP *********************************************************************
testhost : ok=10 changed=7 unreachable=0 failed=0 skipped=1
testhost : ok=11 changed=8 unreachable=0 failed=0 skipped=1 rescued=1 ignored=1

View file

@ -19,6 +19,12 @@ changed: [testhost] => (item=foo-1)
changed: [testhost] => (item=foo-2)
changed: [testhost] => (item=foo-3)
TASK [EXPECTED FAILURE Failed task to be rescued] ******************************
fatal: [testhost]: FAILED! => {"changed": false, "msg": "Failed as requested from task"}
TASK [Rescue task] *************************************************************
changed: [testhost]
RUNNING HANDLER [Test handler 1] ***********************************************
changed: [testhost]
@ -37,5 +43,5 @@ TASK [Second free task] ********************************************************
changed: [testhost]
PLAY RECAP *********************************************************************
testhost : ok=10 changed=7 unreachable=0 failed=0 skipped=1
testhost : ok=11 changed=8 unreachable=0 failed=0 skipped=1 rescued=1 ignored=1

View file

@ -16,6 +16,12 @@ changed: [testhost] => (item=foo-1)
changed: [testhost] => (item=foo-2)
changed: [testhost] => (item=foo-3)
TASK [EXPECTED FAILURE Failed task to be rescued] ******************************
fatal: [testhost]: FAILED! => {"changed": false, "msg": "Failed as requested from task"}
TASK [Rescue task] *************************************************************
changed: [testhost]
RUNNING HANDLER [Test handler 1] ***********************************************
changed: [testhost]
@ -31,5 +37,5 @@ TASK [Second free task] ********************************************************
changed: [testhost]
PLAY RECAP *********************************************************************
testhost : ok=10 changed=7 unreachable=0 failed=0 skipped=1
testhost : ok=11 changed=8 unreachable=0 failed=0 skipped=1 rescued=1 ignored=1

View file

@ -33,6 +33,14 @@
- 3
loop_control:
label: foo-{{ item }}
- block:
- name: EXPECTED FAILURE Failed task to be rescued
fail:
rescue:
- name: Rescue task
command: echo rescued
handlers:
- name: Test handler 1
command: echo foo