Add info about loop based on jinja2 loop var (#42134)

* Add info about loop based on jinja2 loop var

* ansible_loop

* Update test count

* Add extended loop_control that defines whether ansible_loop should be added

* Extended needs to be defaulted

* Revert "Update test count"

This reverts commit f1e93ee469825f4cdcd90fb28667d29aa088275c.

* Add docs about loop_control.extended

* Add revindex and revindex0

* Document ansible_loop in special vars

* Add changelog fragment

* Add tests, change items to allitems so that dot notation works, fix logic error with previtem
pull/49666/head
Matt Martz 6 years ago committed by GitHub
parent 597c258e0e
commit 9007dbec2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,2 @@
minor_changes:
- loop_control - Add new ``extended`` option to return extended loop information (https://github.com/ansible/ansible/pull/42134)

@ -25,6 +25,9 @@ ansible_inventory_sources
ansible_limit
Contents of the ``--limit`` CLI option for the current execution of Ansible
ansible_loop
A dictionary/map containing extended loop information when enabled via ``loop_control.extended``
ansible_play_batch
List of active hosts in the current play run limited by the serial, aka 'batch'. Failed/Unreachable hosts are not considered 'active'.

@ -330,6 +330,30 @@ If you need to keep track of where you are in a loop, you can use the ``index_va
loop_control:
index_var: my_idx
.. versionadded:: 2.8
As of Ansible 2.8 you can get extended loop information using the ``extended`` option to loop control. This option will expose the following information.
========================== ===========
Variable Description
-------------------------- -----------
``ansible_loop.allitems`` The list of all items in the loop
``ansible_loop.index`` The current iteration of the loop. (1 indexed)
``ansible_loop.index0`` The current iteration of the loop. (0 indexed)
``ansible_loop.revindex`` The number of iterations from the end of the loop (1 indexed)
``ansible_loop.revindex0`` The number of iterations from the end of the loop (0 indexed)
``ansible_loop.first`` ``True`` if first iteration
``ansible_loop.last`` ``True`` if last iteration
``ansible_loop.length`` The number of items in the loop
``ansible_loop.previtem`` The item from the previous iteration of the loop. Undefined during the first iteration.
``ansible_loop.nextitem`` The item from the following iteration of the loop. Undefined during the last iteration.
========================== ===========
::
loop_control:
extended: yes
Migrating from with_X to loop
`````````````````````````````

@ -287,6 +287,7 @@ class TaskExecutor:
index_var = None
label = None
loop_pause = 0
extended = False
templar = Templar(loader=self._loader, shared_loader_obj=self._shared_loader_obj, variables=self._job_vars)
# FIXME: move this to the object itself to allow post_validate to take care of templating (loop_control.post_validate)
@ -294,6 +295,7 @@ class TaskExecutor:
loop_var = templar.template(self._task.loop_control.loop_var)
index_var = templar.template(self._task.loop_control.index_var)
loop_pause = templar.template(self._task.loop_control.pause)
extended = templar.template(self._task.loop_control.extended)
# This may be 'None',so it is tempalted below after we ensure a value and an item is assigned
label = self._task.loop_control.label
@ -313,11 +315,30 @@ class TaskExecutor:
items = self._squash_items(items, loop_var, task_vars)
no_log = False
items_len = len(items)
for item_index, item in enumerate(items):
task_vars[loop_var] = item
if index_var:
task_vars[index_var] = item_index
if extended:
task_vars['ansible_loop'] = {
'allitems': items,
'index': item_index + 1,
'index0': item_index,
'first': item_index == 0,
'last': item_index + 1 == items_len,
'length': items_len,
'revindex': items_len - item_index,
'revindex0': items_len - item_index - 1,
}
try:
task_vars['ansible_loop']['nextitem'] = items[item_index + 1]
except IndexError:
pass
if item_index - 1 >= 0:
task_vars['ansible_loop']['previtem'] = items[item_index - 1]
# Update template vars to reflect current loop iteration
templar.set_available_variables(task_vars)
@ -355,6 +376,9 @@ class TaskExecutor:
res[loop_var] = item
if index_var:
res[index_var] = item_index
if extended:
res['ansible_loop'] = task_vars['ansible_loop']
res['_ansible_item_result'] = True
res['_ansible_ignore_errors'] = task_fields.get('ignore_errors')

@ -29,6 +29,7 @@ class LoopControl(FieldAttributeBase):
_index_var = FieldAttribute(isa='str')
_label = FieldAttribute(isa='str')
_pause = FieldAttribute(isa='int', default=0)
_extended = FieldAttribute(isa='bool')
def __init__(self):
super(LoopControl, self).__init__()

@ -268,3 +268,66 @@
things:
- !unsafe foo
- !unsafe bar
- name: extended loop info
assert:
that:
- ansible_loop.nextitem == 'orange'
- ansible_loop.index == 1
- ansible_loop.index0 == 0
- ansible_loop.first
- not ansible_loop.last
- ansible_loop.previtem is undefined
- ansible_loop.allitems == ['apple', 'orange', 'banana']
- ansible_loop.revindex == 3
- ansible_loop.revindex0 == 2
- ansible_loop.length == 3
loop:
- apple
- orange
- banana
loop_control:
extended: true
when: item == 'apple'
- name: extended loop info 2
assert:
that:
- ansible_loop.nextitem == 'banana'
- ansible_loop.index == 2
- ansible_loop.index0 == 1
- not ansible_loop.first
- not ansible_loop.last
- ansible_loop.previtem == 'apple'
- ansible_loop.allitems == ['apple', 'orange', 'banana']
- ansible_loop.revindex == 2
- ansible_loop.revindex0 == 1
- ansible_loop.length == 3
loop:
- apple
- orange
- banana
loop_control:
extended: true
when: item == 'orange'
- name: extended loop info 3
assert:
that:
- ansible_loop.nextitem is undefined
- ansible_loop.index == 3
- ansible_loop.index0 == 2
- not ansible_loop.first
- ansible_loop.last
- ansible_loop.previtem == 'orange'
- ansible_loop.allitems == ['apple', 'orange', 'banana']
- ansible_loop.revindex == 1
- ansible_loop.revindex0 == 0
- ansible_loop.length == 3
loop:
- apple
- orange
- banana
loop_control:
extended: true
when: item == 'banana'

Loading…
Cancel
Save