add_host/group_by: fix using changed_when in a loop (#71719)

Fixes #71627
Fixes #75971
pull/76955/head
Martin Krizek 3 years ago committed by GitHub
parent 2749d9fbf9
commit 394d216922
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,2 @@
bugfixes:
- "add_host/group_by: fix using changed_when in a loop (https://github.com/ansible/ansible/issues/71627)"

@ -71,6 +71,7 @@ _ACTION_INCLUDE = add_internal_fqcns(('include', ))
_ACTION_INCLUDE_ROLE = add_internal_fqcns(('include_role', ))
_ACTION_INCLUDE_TASKS = add_internal_fqcns(('include_tasks', ))
_ACTION_INCLUDE_VARS = add_internal_fqcns(('include_vars', ))
_ACTION_INVENTORY_TASKS = add_internal_fqcns(('add_host', 'group_by'))
_ACTION_META = add_internal_fqcns(('meta', ))
_ACTION_SET_FACT = add_internal_fqcns(('set_fact', ))
_ACTION_SETUP = add_internal_fqcns(('setup', ))

@ -393,7 +393,8 @@ class TaskExecutor:
else:
if getattr(self._task, 'diff', False):
self._final_q.send_callback('v2_on_file_diff', tr)
self._final_q.send_callback('v2_runner_item_on_ok', tr)
if self._task.action not in C._ACTION_INVENTORY_TASKS:
self._final_q.send_callback('v2_runner_item_on_ok', tr)
results.append(res)
del task_vars[loop_var]

@ -268,6 +268,14 @@ class Task(Base, Conditional, Taggable, CollectionSearch):
e.message += '\nThis error can be suppressed as a warning using the "invalid_task_attribute_failed" configuration'
raise e
def _validate_changed_when(self, attr, name, value):
if not isinstance(value, list):
setattr(self, name, [value])
def _validate_failed_when(self, attr, name, value):
if not isinstance(value, list):
setattr(self, name, [value])
def post_validate(self, templar):
'''
Override of base class post_validate, to also do final validation on

@ -76,20 +76,37 @@ class StrategySentinel:
_sentinel = StrategySentinel()
def post_process_whens(result, task, templar):
def post_process_whens(result, task, templar, task_vars):
cond = None
if task.changed_when:
cond = Conditional(loader=templar._loader)
cond.when = task.changed_when
result['changed'] = cond.evaluate_conditional(templar, templar.available_variables)
with templar.set_temporary_context(available_variables=task_vars):
cond = Conditional(loader=templar._loader)
cond.when = task.changed_when
result['changed'] = cond.evaluate_conditional(templar, templar.available_variables)
if task.failed_when:
if cond is None:
cond = Conditional(loader=templar._loader)
cond.when = task.failed_when
failed_when_result = cond.evaluate_conditional(templar, templar.available_variables)
result['failed_when_result'] = result['failed'] = failed_when_result
with templar.set_temporary_context(available_variables=task_vars):
if cond is None:
cond = Conditional(loader=templar._loader)
cond.when = task.failed_when
failed_when_result = cond.evaluate_conditional(templar, templar.available_variables)
result['failed_when_result'] = result['failed'] = failed_when_result
def _get_item_vars(result, task):
item_vars = {}
if task.loop or task.loop_with:
loop_var = result.get('ansible_loop_var', 'item')
index_var = result.get('ansible_index_var')
if loop_var in result:
item_vars[loop_var] = result[loop_var]
if index_var and index_var in result:
item_vars[index_var] = result[index_var]
if '_ansible_item_label' in result:
item_vars['_ansible_item_label'] = result['_ansible_item_label']
if 'ansible_loop' in result:
item_vars['ansible_loop'] = result['ansible_loop']
return item_vars
def results_thread_main(strategy):
@ -680,12 +697,32 @@ class StrategyBase:
# this task added a new host (add_host module)
new_host_info = result_item.get('add_host', dict())
self._add_host(new_host_info, result_item)
post_process_whens(result_item, original_task, handler_templar)
elif 'add_group' in result_item:
# this task added a new group (group_by module)
self._add_group(original_host, result_item)
post_process_whens(result_item, original_task, handler_templar)
if 'add_host' in result_item or 'add_group' in result_item:
item_vars = _get_item_vars(result_item, original_task)
found_task_vars = self._queued_task_cache.get((original_host.name, task_result._task._uuid))['task_vars']
if item_vars:
all_task_vars = combine_vars(found_task_vars, item_vars)
else:
all_task_vars = found_task_vars
all_task_vars[original_task.register] = wrap_var(result_item)
post_process_whens(result_item, original_task, handler_templar, all_task_vars)
if original_task.loop or original_task.loop_with:
new_item_result = TaskResult(
task_result._host,
task_result._task,
result_item,
task_result._task_fields,
)
self._tqm.send_callback('v2_runner_item_on_ok', new_item_result)
if result_item.get('changed', False):
task_result._result['changed'] = True
if result_item.get('failed', False):
task_result._result['failed'] = True
if 'ansible_facts' in result_item and original_task.action not in C._ACTION_DEBUG:
# if delegated fact and we are delegating facts, we need to change target host for them

@ -157,3 +157,20 @@
assert:
that:
- badinput is failed
- name: Add hosts in a loop
add_host:
name: 'host_{{item}}'
loop:
- 1
- 2
- 2
register: add_host_loop_res
- name: verify correct changed results
assert:
that:
- add_host_loop_res.results[0] is changed
- add_host_loop_res.results[1] is changed
- add_host_loop_res.results[2] is not changed
- add_host_loop_res is changed

@ -71,3 +71,41 @@
- invalid_conditional is failed
- invalid_conditional.stdout is defined
- invalid_conditional.changed_when_result is contains('boomboomboom')
- add_host:
name: 'host_{{item}}'
loop:
- 1
- 2
changed_when: item == 2
register: add_host_loop_res
- assert:
that:
- add_host_loop_res.results[0] is not changed
- add_host_loop_res.results[1] is changed
- add_host_loop_res is changed
- group_by:
key: "test_{{ item }}"
loop:
- 1
- 2
changed_when: item == 2
register: group_by_loop_res
- assert:
that:
- group_by_loop_res.results[0] is not changed
- group_by_loop_res.results[1] is changed
- group_by_loop_res is changed
- name: use changed in changed_when
add_host:
name: 'host_3'
changed_when: add_host_loop_res is changed
register: add_host_loop_res
- assert:
that:
- add_host_loop_res is changed

Loading…
Cancel
Save