From a4a8e10d19e1704781f87803738756f6f5c4666c Mon Sep 17 00:00:00 2001 From: Martin Krizek Date: Fri, 25 Oct 2024 10:41:50 +0200 Subject: [PATCH] Simplify global loop result processing --- lib/ansible/executor/task_executor.py | 79 +++++++++++++-------------- lib/ansible/playbook/base.py | 4 +- 2 files changed, 39 insertions(+), 44 deletions(-) diff --git a/lib/ansible/executor/task_executor.py b/lib/ansible/executor/task_executor.py index ff1c33871f2..035616ab435 100644 --- a/lib/ansible/executor/task_executor.py +++ b/lib/ansible/executor/task_executor.py @@ -26,6 +26,7 @@ from ansible.playbook.conditional import Conditional from ansible.playbook.task import Task from ansible.plugins import get_plugin_class from ansible.plugins.loader import become_loader, cliconf_loader, connection_loader, httpapi_loader, netconf_loader, terminal_loader +from ansible.plugins.test.core import unreachable from ansible.template import Templar from ansible.utils.collection_loader import AnsibleCollectionConfig from ansible.utils.listify import listify_lookup_plugin_terms @@ -126,49 +127,43 @@ class TaskExecutor: self._loop_eval_error = e if items is not None: - if len(items) > 0: - item_results = self._run_loop(items) - - # create the overall result item - res = dict(results=item_results) - - # loop through the item results and set the global changed/failed/skipped result flags based on any item. - res['skipped'] = True - for item in item_results: - if 'changed' in item and item['changed'] and not res.get('changed'): - res['changed'] = True - if res['skipped'] and ('skipped' not in item or ('skipped' in item and not item['skipped'])): - res['skipped'] = False - if 'failed' in item and item['failed']: - item_ignore = item.pop('_ansible_ignore_errors') - if not res.get('failed'): - res['failed'] = True - res['msg'] = 'One or more items failed' - self._task.ignore_errors = item_ignore - elif self._task.ignore_errors and not item_ignore: - self._task.ignore_errors = item_ignore - if 'unreachable' in item and item['unreachable']: - item_ignore_unreachable = item.pop('_ansible_ignore_unreachable') - if not res.get('unreachable'): - res['unreachable'] = True - self._task.ignore_unreachable = item_ignore_unreachable - elif self._task.ignore_unreachable and not item_ignore_unreachable: - self._task.ignore_unreachable = item_ignore_unreachable - - # ensure to accumulate these - for array in ['warnings', 'deprecations']: - if array in item and item[array]: - if array not in res: - res[array] = [] - if not isinstance(item[array], list): - item[array] = [item[array]] - res[array] = res[array] + item[array] - del item[array] - - if not res.get('failed', False): - res['msg'] = 'All items completed' - if res['skipped']: + if items: + res = {'results': self._run_loop(items)} + # set the global changed/failed/skipped result flags based on any item + warnings = set() + deprecations = set() + skipped = True + changed = failed = unreachable = False + for item in res['results']: + skipped &= item.get('skipped', False) + changed |= item.get('changed', False) + failed |= (failed_item := item.get('failed', False)) + unreachable |= (unreachable_item := item.get('unreachable', False)) + + if failed_item: + self._task.ignore_errors |= item.pop('_ansible_ignore_errors') + if unreachable_item: + self._task.ignore_unreachable |= item.pop('_ansible_ignore_unreachable') + + warnings.update(item.pop('warnings', [])) + deprecations.update(item.pop('deprecations', [])) + + if warnings: + res['warnings'] = list(warnings) + if deprecations: + res['deprecations'] = list(deprecations) + + if skipped: res['msg'] = 'All items skipped' + else: + res['msg'] = 'One or more items failed' if failed else 'All items completed' + + res.update( + skipped=skipped, + changed=changed, + failed=failed, + unreachable=unreachable, + ) else: res = dict(changed=False, skipped=True, skipped_reason='No items in the list', results=[]) else: diff --git a/lib/ansible/playbook/base.py b/lib/ansible/playbook/base.py index a762548fddf..bd921eb3617 100644 --- a/lib/ansible/playbook/base.py +++ b/lib/ansible/playbook/base.py @@ -722,8 +722,8 @@ class Base(FieldAttributeBase): environment = FieldAttribute(isa='list', extend=True, prepend=True) no_log = FieldAttribute(isa='bool', default=C.DEFAULT_NO_LOG) run_once = FieldAttribute(isa='bool') - ignore_errors = FieldAttribute(isa='bool') - ignore_unreachable = FieldAttribute(isa='bool') + ignore_errors = FieldAttribute(isa='bool', default=False) + ignore_unreachable = FieldAttribute(isa='bool', default=False) check_mode = FieldAttribute(isa='bool', default=context.cliargs_deferred_get('check')) diff = FieldAttribute(isa='bool', default=context.cliargs_deferred_get('diff')) any_errors_fatal = FieldAttribute(isa='bool', default=C.ANY_ERRORS_FATAL)