diff --git a/docs/docsite/rst/playbooks_loops.rst b/docs/docsite/rst/playbooks_loops.rst index 2f8fa0d10f..06838ea3d9 100644 --- a/docs/docsite/rst/playbooks_loops.rst +++ b/docs/docsite/rst/playbooks_loops.rst @@ -287,6 +287,19 @@ Another option to loop control is ``pause``, which allows you to control the tim loop_control: pause: 3 +.. versionadded:: 2.7 + +If you need to keep track of where you are in a loop, you can use the ``index_var`` option to loop control to specify a variable name to contain the current loop index.:: + + - name: count our fruit + debug: + msg: "{{ item }} with index {{ my_idx }}" + loop: + - apple + - banana + - pear + loop_control: + index_var: my_idx .. _loops_and_includes_2.0: diff --git a/lib/ansible/executor/task_executor.py b/lib/ansible/executor/task_executor.py index 8a0f0b7c8f..7f5c43e8b6 100644 --- a/lib/ansible/executor/task_executor.py +++ b/lib/ansible/executor/task_executor.py @@ -269,10 +269,12 @@ class TaskExecutor: task_vars = self._job_vars loop_var = 'item' + index_var = None label = None loop_pause = 0 if self._task.loop_control: loop_var = self._task.loop_control.loop_var + index_var = self._task.loop_control.index_var loop_pause = self._task.loop_control.pause # the these may be 'None', so we still need to default to something useful label = self._task.loop_control.label or ('{{' + loop_var + '}}') @@ -287,8 +289,10 @@ class TaskExecutor: # Only squash with 'with_:' not with the 'loop:', 'magic' squashing can be removed once with_ loops are items = self._squash_items(items, loop_var, task_vars) - for item in items: + for item_index, item in enumerate(items): task_vars[loop_var] = item + if index_var: + task_vars[index_var] = item_index # pause between loop iterations if loop_pause and ran_once: @@ -316,6 +320,8 @@ class TaskExecutor: # now update the result with the item info, and append the result # to the list of results res[loop_var] = item + if index_var: + res[index_var] = item_index res['_ansible_item_result'] = True res['_ansible_ignore_errors'] = task_fields.get('ignore_errors') diff --git a/lib/ansible/playbook/included_file.py b/lib/ansible/playbook/included_file.py index f565a4aa0c..7d9bd4e611 100644 --- a/lib/ansible/playbook/included_file.py +++ b/lib/ansible/playbook/included_file.py @@ -78,10 +78,14 @@ class IncludedFile: include_variables = include_result.get('include_variables', dict()) loop_var = 'item' + index_var = None if original_task.loop_control: loop_var = original_task.loop_control.loop_var + index_var = original_task.loop_control.index_var if loop_var in include_result: task_vars[loop_var] = include_variables[loop_var] = include_result[loop_var] + if index_var and index_var in include_result: + task_vars[index_var] = include_variables[index_var] = include_result[index_var] if original_task.action in ('include', 'include_tasks'): include_file = None diff --git a/lib/ansible/playbook/loop_control.py b/lib/ansible/playbook/loop_control.py index 7fe0ca50ab..b34c6552fe 100644 --- a/lib/ansible/playbook/loop_control.py +++ b/lib/ansible/playbook/loop_control.py @@ -26,6 +26,7 @@ from ansible.playbook.base import Base class LoopControl(Base): _loop_var = FieldAttribute(isa='str', default='item') + _index_var = FieldAttribute(isa='str') _label = FieldAttribute(isa='str') _pause = FieldAttribute(isa='int', default=0) diff --git a/test/integration/targets/loops/tasks/index_var_tasks.yml b/test/integration/targets/loops/tasks/index_var_tasks.yml new file mode 100644 index 0000000000..fa9a5bdfc1 --- /dev/null +++ b/test/integration/targets/loops/tasks/index_var_tasks.yml @@ -0,0 +1,3 @@ +- name: check that index var exists inside included tasks file + assert: + that: my_idx == item|int diff --git a/test/integration/targets/loops/tasks/main.yml b/test/integration/targets/loops/tasks/main.yml index 5d61df6de0..72dc1af1e3 100644 --- a/test/integration/targets/loops/tasks/main.yml +++ b/test/integration/targets/loops/tasks/main.yml @@ -1,3 +1,7 @@ +# +# loop_control/pause +# + - name: Measure time before shell: date +%s register: before @@ -146,3 +150,29 @@ that: - 'results10["results"][0]["ping"] == "Hello World"' - 'results10["results"][1]["ping"] == "Olá Mundo"' + +# +# loop_control/index_var +# + +- name: check that the index var is created and increments as expected + assert: + that: my_idx == item|int + with_sequence: start=0 count=3 + loop_control: + index_var: my_idx + +- name: check that value of index var matches position of current item in source list + assert: + that: 'test_var.index(item) == my_idx' + vars: + test_var: ['a', 'b', 'c'] + with_items: "{{ test_var }}" + loop_control: + index_var: my_idx + +- name: check index var with included tasks file + include_tasks: index_var_tasks.yml + with_sequence: start=0 count=3 + loop_control: + index_var: my_idx