Repository: ansible/ansible

Issue:
Task name is not templated in "FAILED - RETRYING: TASK:" message
<!---
Please do not report issues/requests related to Ansible modules here !!

Report them to the appropriate modules-core or modules-extras project:
  - https://github.com/ansible/ansible-modules-core/issues
  - https://github.com/ansible/ansible-modules-extras/issues

Also verify first that your issue/request is not already reported in GitHub
-->
##### ISSUE TYPE

<!--- Pick one below and delete the rest: -->
- Feature Idea
##### COMPONENT NAME

<!--- Name of the plugin/task/feature -->

[CallbackModule](https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/callback/default.py) or [StrategyBase](https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/strategy/__init__.py)

This is the method printing the message in question:
CallbackModule.v2_runner_retry
and it's called from
StrategyBase._process_pending_results
##### ANSIBLE VERSION

<!--- Paste verbatim output from “ansible --version” between quotes below -->

```
ansible 2.3.0 (devel dceb2a0393) last updated 2016/10/28 09:48:34 (GMT +200)
  lib/ansible/modules/core: (detached HEAD 124bb92416) last updated 2016/10/28 09:48:37 (GMT +200)
  lib/ansible/modules/extras: (detached HEAD 8ffe314ea5) last updated 2016/10/25 10:21:06 (GMT +200)
  config file = /etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides

```
##### CONFIGURATION

<!---
Mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).
-->

retry_files_enabled = False
pipelining = True
##### OS / ENVIRONMENT

<!---
Mention the OS you are running Ansible from, and the OS you are
managing, or say “N/A” for anything that is not platform-specific.
-->

CentOS release 6.8 (Final)
##### SUMMARY

<!--- Explain the problem briefly -->

Not sure if this is a bug report or a feature request, but it would be nice to have the variables in task name templated when it's printed in "FAILED - RETRYING: TASK:" message
##### STEPS TO REPRODUCE

<!---
For bugs, show exactly how to reproduce the problem.
For new features, show how the feature would be used.
-->

Put a variable in name of a task that is defined to retry until a condition is fulfilled and make it retry at least once.

<!--- Paste example playbooks or commands between quotes below -->

```

---
- hosts: localhost
  tasks:
  - name: First run
    command: echo 1
    register: result

  - name: Previous result {{ result.stdout }}
    command: echo 2
    register: result
    retries: 1
    delay: 1
    until: False
```

<!--- You can also paste gist.github.com links for larger files -->
##### EXPECTED RESULTS

<!--- What did you expect to happen when running the steps above? -->

```
PLAY [localhost] ***************************************************************

TASK [setup] *******************************************************************
ok: [localhost]

TASK [First run] ***************************************************************
changed: [localhost]

TASK [Previous result 1] *******************************************************
FAILED - RETRYING: TASK: Previous result 2 (1 retries left).
fatal: [localhost]: FAILED! => {"attempts": 1, "changed": true, "cmd": ["echo", "2"], "delta": "0:00:00.004803", "end": "2016-10-28 16:34:03.315666", "failed": true, "rc": 0, "start": "2016-10-28 16:34:03.310863", "stderr": "", "stdout": "2", "stdout_lines": ["2"], "warnings": []}

```
##### ACTUAL RESULTS

<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes below -->

```
PLAY [localhost] ***************************************************************

TASK [setup] *******************************************************************
ok: [localhost]

TASK [First run] ***************************************************************
changed: [localhost]

TASK [Previous result 1] *******************************************************
FAILED - RETRYING: TASK: Previous result {{ result.stdout }} (1 retries left).
fatal: [localhost]: FAILED! => {"attempts": 1, "changed": true, "cmd": ["echo", "2"], "delta": "0:00:00.004803", "end": "2016-10-28 16:34:03.315666", "failed": true, "rc": 0, "start": "2016-10-28 16:34:03.310863", "stderr": "", "stdout": "2", "stdout_lines": ["2"], "warnings": []}

```



Discussion:
I think that is probably a bug with the default callback's 'v2_on_retry', since it's changed since stable-2.1:

```
[fedora-23:ansible (stable-2.1 % u-9)]$ ansible-playbook -v task_expand.yml 
Using /home/adrian/ansible/ansible.cfg as config file
 [WARNING]: provided hosts list is empty, only localhost is available


PLAY [localhost] ***************************************************************

TASK [setup] *******************************************************************
ok: [localhost]

TASK [First run] ***************************************************************
changed: [localhost] => {"changed": true, "cmd": ["echo", "1"], "delta": "0:00:00.001773", "end": "2016-10-31 13:26:41.368354", "rc": 0, "start": "2016-10-31 13:26:41.366581", "stderr": "", "stdout": "1", "stdout_lines": ["1"], "warnings": []}

TASK [Previous result 1] *******************************************************
FAILED - RETRYING: TASK: Previous result 1 (3 retries left).
FAILED - RETRYING: TASK: Previous result 1 (2 retries left).
FAILED - RETRYING: TASK: Previous result 1 (1 retries left).
fatal: [localhost]: FAILED! => {"changed": true, "cmd": ["echo", "2"], "delta": "0:00:00.001913", "end": "2016-10-31 13:26:44.864595", "failed": true, "rc": 0, "start": "2016-10-31 13:26:44.862682", "stderr": "", "stdout": "2", "stdout_lines": ["2"], "warnings": []}

NO MORE HOSTS LEFT *************************************************************
    to retry, use: --limit @/home/adrian/src/ansible/task_expand.retry

PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=1   
```

devel/

```
[fedora-23:ansible (devel % u=)]$ ansible-playbook -v task_expand.yml 
Using /home/adrian/ansible/ansible.cfg as config file
 [WARNING]: provided hosts list is empty, only localhost is available


PLAY [localhost] ***************************************************************

TASK [setup] *******************************************************************
ok: [localhost]

TASK [First run] ***************************************************************
changed: [localhost] => {"changed": true, "cmd": ["echo", "1"], "delta": "0:00:00.002133", "end": "2016-10-31 13:26:54.500696", "rc": 0, "start": "2016-10-31 13:26:54.498563", "stderr": "", "stdout": "1", "stdout_lines": ["1"], "warnings": []}

TASK [Previous result 1] *******************************************************
FAILED - RETRYING: TASK: Previous result {{ result.stdout }} (3 retries left).
FAILED - RETRYING: TASK: Previous result {{ result.stdout }} (2 retries left).
FAILED - RETRYING: TASK: Previous result {{ result.stdout }} (1 retries left).
fatal: [localhost]: FAILED! => {"attempts": 3, "changed": true, "cmd": ["echo", "2"], "delta": "0:00:00.001840", "end": "2016-10-31 13:26:57.998997", "failed": true, "rc": 0, "start": "2016-10-31 13:26:57.997157", "stderr": "", "stdout": "2", "stdout_lines": ["2"], "warnings": []}
    to retry, use: --limit @/home/adrian/src/ansible/task_expand.retry

PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=1   

```

stable-2.2 looks like it's busted as well:

```
[fedora-23:ansible (stable-2.2 % u-21)]$ ansible-playbook -v task_expand.yml 
Using /home/adrian/ansible/ansible.cfg as config file
 [WARNING]: provided hosts list is empty, only localhost is available


PLAY [localhost] ***************************************************************

TASK [setup] *******************************************************************
ok: [localhost]

TASK [First run] ***************************************************************
changed: [localhost] => {"changed": true, "cmd": ["echo", "1"], "delta": "0:00:00.002077", "end": "2016-10-31 13:29:02.154814", "rc": 0, "start": "2016-10-31 13:29:02.152737", "stderr": "", "stdout": "1", "stdout_lines": ["1"], "warnings": []}

TASK [Previous result 1] *******************************************************
FAILED - RETRYING: TASK: Previous result {{ result.stdout }} (3 retries left).
FAILED - RETRYING: TASK: Previous result {{ result.stdout }} (2 retries left).
FAILED - RETRYING: TASK: Previous result {{ result.stdout }} (1 retries left).
fatal: [localhost]: FAILED! => {"attempts": 3, "changed": true, "cmd": ["echo", "2"], "delta": "0:00:00.002137", "end": "2016-10-31 13:29:05.661615", "failed": true, "rc": 0, "start": "2016-10-31 13:29:05.659478", "stderr": "", "stdout": "2", "stdout_lines": ["2"], "warnings": []}
    to retry, use: --limit @/home/adrian/src/ansible/task_expand.retry

PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=1   
```

git bisect points to:

```
fedora-25:ansible ((80385a4...) %|BISECTING)]$ git bisect good
d2b3b2c03e2934b126f5701e5f6e25821e2dbe35 is the first bad commit
commit d2b3b2c03e2934b126f5701e5f6e25821e2dbe35
Author: James Cammarata <jimi@sngx.net>
Date:   Sun Jul 31 03:23:28 2016 -0500

    Performance improvements

:040000 040000 202b7ef53022e91aab6351467238ab99eb4d301c 82393f4b1cb0e699933a28927e46376a55d5070d M      lib
:040000 040000 97f671dc4123188332655745403fcd78700397b3 bf921947d207d4a82045602705bc5d8e9bb9a19d M      test
```

Resolving patch:
diff --git a/lib/ansible/executor/task_result.py b/lib/ansible/executor/task_result.py
--- a/lib/ansible/executor/task_result.py
+++ b/lib/ansible/executor/task_result.py
@@ -21,6 +21,7 @@
 
 from ansible.parsing.dataloader import DataLoader
 
+
 class TaskResult:
     '''
     This class is responsible for interpreting the resulting data
@@ -42,6 +43,10 @@ def __init__(self, host, task, return_data, task_fields=None):
         else:
             self._task_fields = task_fields
 
+    @property
+    def task_name(self):
+        return self._task_fields.get('name', None) or self._task.get_name()
+
     def is_changed(self):
         return self._check_key('changed')
 
diff --git a/lib/ansible/plugins/callback/default.py b/lib/ansible/plugins/callback/default.py
--- a/lib/ansible/plugins/callback/default.py
+++ b/lib/ansible/plugins/callback/default.py
@@ -301,7 +301,8 @@ def v2_playbook_on_start(self, playbook):
                         self._display.vvvv('%s: %s' % (option,val))
 
     def v2_runner_retry(self, result):
-        msg = "FAILED - RETRYING: %s (%d retries left)." % (result._task, result._result['retries'] - result._result['attempts'])
+        task_name = result.task_name or result._task
+        msg = "FAILED - RETRYING: %s (%d retries left)." % (task_name, result._result['retries'] - result._result['attempts'])
         if (self._display.verbosity > 2 or '_ansible_verbose_always' in result._result) and not '_ansible_verbose_override' in result._result:
             msg += "Result was: %s" % self._dump_results(result._result)
         self._display.display(msg, color=C.COLOR_DEBUG)


Test patch:
diff --git a/test/integration/Makefile b/test/integration/Makefile
--- a/test/integration/Makefile
+++ b/test/integration/Makefile
@@ -27,7 +27,10 @@ UNAME := $(shell uname | tr '[:upper:]' '[:lower:]')
 
 all: other non_destructive destructive
 
-other: ansible test_test_infra parsing test_var_blending test_var_precedence unicode test_templating_settings environment test_as includes blocks pull_run pull_no_127 pull_limit_inventory check_mode test_hash test_handlers test_group_by test_vault test_tags test_lookup_paths no_log test_gathering_facts test_binary_modules_posix test_hosts_field test_lookup_properties args test_jinja2_groupby
+other: ansible test_test_infra parsing test_var_blending test_var_precedence unicode test_templating_settings \
+	environment test_as includes blocks pull_run pull_no_127 pull_limit_inventory check_mode test_hash test_handlers \
+	test_group_by test_vault test_tags test_lookup_paths no_log test_gathering_facts test_binary_modules_posix \
+	test_hosts_field test_lookup_properties args test_jinja2_groupby test_callback_retry_task_name
 
 ansible:
 	(cd targets/ansible && ./runme.sh $(TEST_FLAGS))
@@ -326,3 +329,6 @@ test_lookup_properties:
 
 args:
 	(cd targets/args && ./runme.sh $(TEST_FLAGS))
+
+test_callback_retry_task_name:
+	(cd targets/callback_retry_task_name && ./runme.sh $(TEST_FLAGS))
diff --git a/test/integration/targets/callback_retry_task_name/aliases b/test/integration/targets/callback_retry_task_name/aliases
new file mode 100644
--- /dev/null
+++ b/test/integration/targets/callback_retry_task_name/aliases
@@ -0,0 +1 @@
+posix/ci/group3
diff --git a/test/integration/targets/callback_retry_task_name/runme.sh b/test/integration/targets/callback_retry_task_name/runme.sh
new file mode 100755
--- /dev/null
+++ b/test/integration/targets/callback_retry_task_name/runme.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+
+set -eux
+
+# we are looking to verify the callback for v2_retry_runner gets a correct task name, include
+# if the value needs templating based on results of previous tasks
+OUTFILE="callback_retry_task_name.out"
+trap 'rm -rf "${OUTFILE}"' EXIT
+
+EXPECTED_REGEX="^.*TASK.*18236 callback task template fix OUTPUT 2"
+ansible-playbook "$@" -i ../../inventory test.yml | tee "${OUTFILE}"
+echo "Grepping for ${EXPECTED_REGEX} in stdout."
+grep -e "${EXPECTED_REGEX}" "${OUTFILE}"
diff --git a/test/integration/targets/callback_retry_task_name/test.yml b/test/integration/targets/callback_retry_task_name/test.yml
new file mode 100644
--- /dev/null
+++ b/test/integration/targets/callback_retry_task_name/test.yml
@@ -0,0 +1,28 @@
+---
+- hosts: testhost
+  gather_facts: False
+  vars:
+      foo: blippy
+  tasks:
+  - name: First run {{ foo }}
+    command: echo "18236 callback task template fix OUTPUT 1"
+    register: the_result_var
+
+  - block:
+      - name: "{{ the_result_var.stdout }}"
+        command: echo "18236 callback task template fix OUTPUT 2"
+        register: the_result_var
+        retries: 1
+        delay: 1
+        until: False
+        ignore_errors: true
+
+      #  - name: assert task_name was
+
+  - name: "{{ the_result_var.stdout }}"
+    command: echo "18236 callback taskadfadf template fix OUTPUT 3"
+    register: the_result_var
+
+  - name: "{{ the_result_var.stdout }}"
+    debug:
+        msg: "nothing to see here."