mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
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
This commit is contained in:
parent
597c258e0e
commit
9007dbec2f
6 changed files with 117 additions and 0 deletions
2
changelogs/fragments/loop-info.yaml
Normal file
2
changelogs/fragments/loop-info.yaml
Normal file
|
@ -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
|
ansible_limit
|
||||||
Contents of the ``--limit`` CLI option for the current execution of Ansible
|
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
|
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'.
|
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:
|
loop_control:
|
||||||
index_var: my_idx
|
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
|
Migrating from with_X to loop
|
||||||
`````````````````````````````
|
`````````````````````````````
|
||||||
|
|
||||||
|
|
|
@ -287,6 +287,7 @@ class TaskExecutor:
|
||||||
index_var = None
|
index_var = None
|
||||||
label = None
|
label = None
|
||||||
loop_pause = 0
|
loop_pause = 0
|
||||||
|
extended = False
|
||||||
templar = Templar(loader=self._loader, shared_loader_obj=self._shared_loader_obj, variables=self._job_vars)
|
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)
|
# 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)
|
loop_var = templar.template(self._task.loop_control.loop_var)
|
||||||
index_var = templar.template(self._task.loop_control.index_var)
|
index_var = templar.template(self._task.loop_control.index_var)
|
||||||
loop_pause = templar.template(self._task.loop_control.pause)
|
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
|
# 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
|
label = self._task.loop_control.label
|
||||||
|
@ -313,11 +315,30 @@ class TaskExecutor:
|
||||||
items = self._squash_items(items, loop_var, task_vars)
|
items = self._squash_items(items, loop_var, task_vars)
|
||||||
|
|
||||||
no_log = False
|
no_log = False
|
||||||
|
items_len = len(items)
|
||||||
for item_index, item in enumerate(items):
|
for item_index, item in enumerate(items):
|
||||||
task_vars[loop_var] = item
|
task_vars[loop_var] = item
|
||||||
if index_var:
|
if index_var:
|
||||||
task_vars[index_var] = item_index
|
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
|
# Update template vars to reflect current loop iteration
|
||||||
templar.set_available_variables(task_vars)
|
templar.set_available_variables(task_vars)
|
||||||
|
|
||||||
|
@ -355,6 +376,9 @@ class TaskExecutor:
|
||||||
res[loop_var] = item
|
res[loop_var] = item
|
||||||
if index_var:
|
if index_var:
|
||||||
res[index_var] = item_index
|
res[index_var] = item_index
|
||||||
|
if extended:
|
||||||
|
res['ansible_loop'] = task_vars['ansible_loop']
|
||||||
|
|
||||||
res['_ansible_item_result'] = True
|
res['_ansible_item_result'] = True
|
||||||
res['_ansible_ignore_errors'] = task_fields.get('ignore_errors')
|
res['_ansible_ignore_errors'] = task_fields.get('ignore_errors')
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ class LoopControl(FieldAttributeBase):
|
||||||
_index_var = FieldAttribute(isa='str')
|
_index_var = FieldAttribute(isa='str')
|
||||||
_label = FieldAttribute(isa='str')
|
_label = FieldAttribute(isa='str')
|
||||||
_pause = FieldAttribute(isa='int', default=0)
|
_pause = FieldAttribute(isa='int', default=0)
|
||||||
|
_extended = FieldAttribute(isa='bool')
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(LoopControl, self).__init__()
|
super(LoopControl, self).__init__()
|
||||||
|
|
|
@ -268,3 +268,66 @@
|
||||||
things:
|
things:
|
||||||
- !unsafe foo
|
- !unsafe foo
|
||||||
- !unsafe bar
|
- !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…
Reference in a new issue