Fix missing ansible.builtin FQCNs in hardcoded action names (#71824)

* Make sure hard-coded action names also check for FQCN.
* Use _add_internal_fqcn() to avoid hardcoded lists and typoes.
pull/70942/head
Felix Fontein 4 years ago committed by GitHub
parent 79021df2e4
commit da60525610
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,2 @@
bugfixes:
- "Adjust various hard-coded action names to also include their ``ansible.builtin.`` and ``ansible.legacy.`` prefixed version (https://github.com/ansible/ansible/issues/71817, https://github.com/ansible/ansible/issues/71818, https://github.com/ansible/ansible/pull/71824)."

@ -71,7 +71,7 @@ class AdHocCLI(CLI):
'timeout': context.CLIARGS['task_timeout']}
# avoid adding to tasks that don't support it, unless set, then give user an error
if context.CLIARGS['module_name'] not in ('include_role', 'include_tasks') and any(frozenset((async_val, poll))):
if context.CLIARGS['module_name'] not in C._ACTION_ALL_INCLUDE_ROLE_TASKS and any(frozenset((async_val, poll))):
mytask['async_val'] = async_val
mytask['poll'] = poll
@ -120,7 +120,7 @@ class AdHocCLI(CLI):
raise AnsibleOptionsError(err)
# Avoid modules that don't work with ad-hoc
if context.CLIARGS['module_name'] in ('import_playbook',):
if context.CLIARGS['module_name'] in C._ACTION_IMPORT_PLAYBOOK:
raise AnsibleOptionsError("'%s' is not a valid action for ad-hoc commands"
% context.CLIARGS['module_name'])

@ -185,7 +185,7 @@ class ConsoleCLI(CLI, cmd.Cmd):
result = None
try:
check_raw = module in ('command', 'shell', 'script', 'raw')
check_raw = module in C._ACTION_ALLOWS_RAW_ARGS
task = dict(action=dict(module=module, args=parse_kv(module_args, check_raw=check_raw)), timeout=self.task_timeout)
play_ds = dict(
name="Ansible Shell",

@ -8,6 +8,7 @@ __metaclass__ = type
import os
import stat
from ansible import constants as C
from ansible import context
from ansible.cli import CLI
from ansible.cli.arguments import option_helpers as opt_help
@ -170,7 +171,7 @@ class PlaybookCLI(CLI):
if isinstance(task, Block):
taskmsg += _process_block(task)
else:
if task.action == 'meta' and task.implicit:
if task.action in C._ACTION_META and task.implicit:
continue
all_tags.update(task.tags)

@ -17,6 +17,7 @@ from ansible.module_utils._text import to_text
from ansible.module_utils.common.collections import Sequence
from ansible.module_utils.parsing.convert_bool import boolean, BOOLEANS_TRUE
from ansible.module_utils.six import string_types
from ansible.utils.fqcn import add_internal_fqcns
def _warning(msg):
@ -74,10 +75,10 @@ DOCUMENTABLE_PLUGINS = CONFIGURABLE_PLUGINS + ('module', 'strategy')
IGNORE_FILES = ("COPYING", "CONTRIBUTING", "LICENSE", "README", "VERSION", "GUIDELINES") # ignore during module search
INTERNAL_RESULT_KEYS = ('add_host', 'add_group')
LOCALHOST = ('127.0.0.1', 'localhost', '::1')
MODULE_REQUIRE_ARGS = ('command', 'win_command', 'ansible.windows.win_command', 'shell', 'win_shell',
'ansible.windows.win_shell', 'raw', 'script')
MODULE_NO_JSON = ('command', 'win_command', 'ansible.windows.win_command', 'shell', 'win_shell',
'ansible.windows.win_shell', 'raw')
MODULE_REQUIRE_ARGS = tuple(add_internal_fqcns(('command', 'win_command', 'ansible.windows.win_command', 'shell', 'win_shell',
'ansible.windows.win_shell', 'raw', 'script')))
MODULE_NO_JSON = tuple(add_internal_fqcns(('command', 'win_command', 'ansible.windows.win_command', 'shell', 'win_shell',
'ansible.windows.win_shell', 'raw')))
RESTRICTED_RESULT_KEYS = ('ansible_rsync_path', 'ansible_playbook_python', 'ansible_facts')
TREE_DIR = None
VAULT_VERSION_MIN = 1.0
@ -164,3 +165,27 @@ for setting in config.data.get_settings():
for warn in config.WARNINGS:
_warning(warn)
# The following are hard-coded action names
_ACTION_DEBUG = add_internal_fqcns(('debug', ))
_ACTION_IMPORT_PLAYBOOK = add_internal_fqcns(('import_playbook', ))
_ACTION_IMPORT_ROLE = add_internal_fqcns(('import_role', ))
_ACTION_IMPORT_TASKS = add_internal_fqcns(('import_tasks', ))
_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_META = add_internal_fqcns(('meta', ))
_ACTION_SET_FACT = add_internal_fqcns(('set_fact', ))
_ACTION_HAS_CMD = add_internal_fqcns(('command', 'shell', 'script'))
_ACTION_ALLOWS_RAW_ARGS = _ACTION_HAS_CMD + add_internal_fqcns(('raw', ))
_ACTION_ALL_INCLUDES = _ACTION_INCLUDE + _ACTION_INCLUDE_TASKS + _ACTION_INCLUDE_ROLE
_ACTION_ALL_IMPORT_PLAYBOOKS = _ACTION_INCLUDE + _ACTION_IMPORT_PLAYBOOK
_ACTION_ALL_INCLUDE_IMPORT_TASKS = _ACTION_INCLUDE + _ACTION_INCLUDE_TASKS + _ACTION_IMPORT_TASKS
_ACTION_ALL_PROPER_INCLUDE_IMPORT_ROLES = _ACTION_INCLUDE_ROLE + _ACTION_IMPORT_ROLE
_ACTION_ALL_PROPER_INCLUDE_IMPORT_TASKS = _ACTION_INCLUDE_TASKS + _ACTION_IMPORT_TASKS
_ACTION_ALL_INCLUDE_ROLE_TASKS = _ACTION_INCLUDE_ROLE + _ACTION_INCLUDE_TASKS
_ACTION_ALL_INCLUDE_TASKS = _ACTION_INCLUDE + _ACTION_INCLUDE_TASKS
_ACTION_FACT_GATHERING = add_internal_fqcns(('setup', 'gather_facts'))
_ACTION_WITH_CLEAN_FACTS = _ACTION_SET_FACT + _ACTION_INCLUDE_VARS

@ -477,7 +477,7 @@ class TaskExecutor:
# if this task is a TaskInclude, we just return now with a success code so the
# main thread can expand the task list for the given host
if self._task.action in ('include', 'include_tasks'):
if self._task.action in C._ACTION_ALL_INCLUDE_TASKS:
include_args = self._task.args.copy()
include_file = include_args.pop('_raw_params', None)
if not include_file:
@ -487,7 +487,7 @@ class TaskExecutor:
return dict(include=include_file, include_args=include_args)
# if this task is a IncludeRole, we just return now with a success code so the main thread can expand the task list for the given host
elif self._task.action == 'include_role':
elif self._task.action in C._ACTION_INCLUDE_ROLE:
include_args = self._task.args.copy()
return dict(include_args=include_args)
@ -624,7 +624,7 @@ class TaskExecutor:
return failed_when_result
if 'ansible_facts' in result:
if self._task.action in ('set_fact', 'include_vars'):
if self._task.action in C._ACTION_WITH_CLEAN_FACTS:
vars_copy.update(result['ansible_facts'])
else:
# TODO: cleaning of facts should eventually become part of taskresults instead of vars
@ -688,7 +688,7 @@ class TaskExecutor:
variables[self._task.register] = result = wrap_var(result)
if 'ansible_facts' in result:
if self._task.action in ('set_fact', 'include_vars'):
if self._task.action in C._ACTION_WITH_CLEAN_FACTS:
variables.update(result['ansible_facts'])
else:
# TODO: cleaning of facts should eventually become part of taskresults instead of vars

@ -113,7 +113,7 @@ class TaskResult:
result = TaskResult(self._host, self._task, {}, self._task_fields)
# statuses are already reflected on the event type
if result._task and result._task.action in ['debug']:
if result._task and result._task.action in C._ACTION_DEBUG:
# debug is verbose by default to display vars, no need to add invocation
ignore = _IGNORE + ('invocation',)
else:

@ -26,13 +26,14 @@ from ansible.module_utils._text import to_text
from ansible.parsing.splitter import parse_kv, split_args
from ansible.plugins.loader import module_loader, action_loader
from ansible.template import Templar
from ansible.utils.fqcn import add_internal_fqcns
from ansible.utils.sentinel import Sentinel
# For filtering out modules correctly below
FREEFORM_ACTIONS = frozenset(C.MODULE_REQUIRE_ARGS)
RAW_PARAM_MODULES = FREEFORM_ACTIONS.union((
RAW_PARAM_MODULES = FREEFORM_ACTIONS.union(add_internal_fqcns((
'include',
'include_vars',
'include_tasks',
@ -43,16 +44,16 @@ RAW_PARAM_MODULES = FREEFORM_ACTIONS.union((
'group_by',
'set_fact',
'meta',
))
)))
BUILTIN_TASKS = frozenset((
BUILTIN_TASKS = frozenset(add_internal_fqcns((
'meta',
'include',
'include_tasks',
'include_role',
'import_tasks',
'import_role'
))
)))
class ModuleArgsParser:

@ -91,15 +91,19 @@ class Playbook:
self._loader.set_basedir(cur_basedir)
raise AnsibleParserError("playbook entries must be either a valid play or an include statement", obj=entry)
if any(action in entry for action in ('import_playbook', 'include')):
if 'include' in entry:
if any(action in entry for action in C._ACTION_ALL_IMPORT_PLAYBOOKS):
if any(action in entry for action in C._ACTION_INCLUDE):
display.deprecated("'include' for playbook includes. You should use 'import_playbook' instead",
version="2.12", collection_name='ansible.builtin')
pb = PlaybookInclude.load(entry, basedir=self._basedir, variable_manager=variable_manager, loader=self._loader)
if pb is not None:
self._entries.extend(pb._entries)
else:
which = entry.get('import_playbook', entry.get('include', entry))
which = entry
for k in C._ACTION_IMPORT_PLAYBOOK + C._ACTION_INCLUDE:
if k in entry:
which = entry[k]
break
display.display("skipping playbook '%s' due to conditional test failure" % which, color=C.COLOR_SKIP)
else:
entry_obj = Play.load(entry, variable_manager=variable_manager, loader=self._loader, vars=vars)

@ -19,6 +19,7 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import ansible.constants as C
from ansible.errors import AnsibleParserError
from ansible.playbook.attribute import FieldAttribute
from ansible.playbook.base import Base
@ -374,8 +375,8 @@ class Block(Base, Conditional, CollectionSearch, Taggable):
filtered_block = evaluate_block(task)
if filtered_block.has_tasks():
tmp_list.append(filtered_block)
elif ((task.action == 'meta' and task.implicit) or
(task.action == 'include' and task.evaluate_tags([], self._play.skip_tags, all_vars=all_vars)) or
elif ((task.action in C._ACTION_META and task.implicit) or
(task.action in C._ACTION_INCLUDE and task.evaluate_tags([], self._play.skip_tags, all_vars=all_vars)) or
task.evaluate_tags(self._play.only_tags, self._play.skip_tags, all_vars=all_vars)):
tmp_list.append(task)
return tmp_list

@ -128,7 +128,7 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h
# But if it wasn't, we can add the yaml object now to get more detail
raise AnsibleParserError(to_native(e), obj=task_ds, orig_exc=e)
if action in ('include', 'import_tasks', 'include_tasks'):
if action in C._ACTION_ALL_INCLUDE_IMPORT_TASKS:
if use_handlers:
include_class = HandlerTaskInclude
@ -150,9 +150,9 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h
# check to see if this include is dynamic or static:
# 1. the user has set the 'static' option to false or true
# 2. one of the appropriate config options was set
if action == 'include_tasks':
if action in C._ACTION_INCLUDE_TASKS:
is_static = False
elif action == 'import_tasks':
elif action in C._ACTION_IMPORT_TASKS:
is_static = True
elif t.static is not None:
display.deprecated("The use of 'static' has been deprecated. "
@ -166,7 +166,7 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h
if is_static:
if t.loop is not None:
if action == 'import_tasks':
if action in C._ACTION_IMPORT_TASKS:
raise AnsibleParserError("You cannot use loops on 'import_tasks' statements. You should use 'include_tasks' instead.", obj=task_ds)
else:
raise AnsibleParserError("You cannot use 'static' on an include with a loop", obj=task_ds)
@ -248,7 +248,7 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h
# nested includes, and we want the include order printed correctly
display.vv("statically imported: %s" % include_file)
except AnsibleFileNotFound:
if action != 'include' or t.static or \
if action not in C._ACTION_INCLUDE or t.static or \
C.DEFAULT_TASK_INCLUDES_STATIC or \
C.DEFAULT_HANDLER_INCLUDES_STATIC and use_handlers:
raise
@ -286,7 +286,7 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h
tags = tags.split(',')
if len(tags) > 0:
if action in ('include_tasks', 'import_tasks'):
if action in C._ACTION_ALL_PROPER_INCLUDE_IMPORT_TASKS:
raise AnsibleParserError('You cannot specify "tags" inline to the task, it is a task keyword')
if len(ti_copy.tags) > 0:
raise AnsibleParserError(
@ -316,7 +316,7 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h
t.is_static = False
task_list.append(t)
elif action in ('include_role', 'import_role'):
elif action in C._ACTION_ALL_PROPER_INCLUDE_IMPORT_ROLES:
ir = IncludeRole.load(
task_ds,
block=block,
@ -329,7 +329,7 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h
# 1. the user has set the 'static' option to false or true
# 2. one of the appropriate config options was set
is_static = False
if action == 'import_role':
if action in C._ACTION_IMPORT_ROLE:
is_static = True
elif ir.static is not None:
@ -340,7 +340,7 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h
if is_static:
if ir.loop is not None:
if action == 'import_role':
if action in C._ACTION_IMPORT_ROLE:
raise AnsibleParserError("You cannot use loops on 'import_role' statements. You should use 'include_role' instead.", obj=task_ds)
else:
raise AnsibleParserError("You cannot use 'static' on an include_role with a loop", obj=task_ds)

@ -21,6 +21,7 @@ __metaclass__ = type
import os
from ansible import constants as C
from ansible.errors import AnsibleError
from ansible.module_utils._text import to_text
from ansible.playbook.task_include import TaskInclude
@ -67,7 +68,7 @@ class IncludedFile:
original_host = res._host
original_task = res._task
if original_task.action in ('include', 'include_tasks', 'include_role'):
if original_task.action in C._ACTION_ALL_INCLUDES:
if original_task.loop:
if 'results' not in res._result:
continue
@ -111,7 +112,7 @@ class IncludedFile:
templar = Templar(loader=loader, variables=task_vars)
if original_task.action in ('include', 'include_tasks'):
if original_task.action in C._ACTION_ALL_INCLUDE_TASKS:
include_file = None
if original_task:
if original_task.static:

@ -21,6 +21,7 @@ __metaclass__ = type
import os
import ansible.constants as C
from ansible.errors import AnsibleParserError, AnsibleAssertionError
from ansible.module_utils._text import to_bytes
from ansible.module_utils.six import iteritems, string_types
@ -139,7 +140,7 @@ class PlaybookInclude(Base, Conditional, Taggable):
new_ds.ansible_pos = ds.ansible_pos
for (k, v) in iteritems(ds):
if k in ('include', 'import_playbook'):
if k in C._ACTION_ALL_IMPORT_PLAYBOOKS:
self._preprocess_import(ds, new_ds, k, v)
else:
# some basic error checking, to make sure vars are properly

@ -20,6 +20,7 @@ __metaclass__ = type
from os.path import basename
import ansible.constants as C
from ansible.errors import AnsibleParserError
from ansible.playbook.attribute import FieldAttribute
from ansible.playbook.block import Block
@ -127,7 +128,7 @@ class IncludeRole(TaskInclude):
if ir._role_name is None:
raise AnsibleParserError("'name' is a required field for %s." % ir.action, obj=data)
if 'public' in ir.args and ir.action != 'include_role':
if 'public' in ir.args and ir.action not in C._ACTION_INCLUDE_ROLE:
raise AnsibleParserError('Invalid options for %s: public' % ir.action, obj=data)
# validate bad args, otherwise we silently ignore
@ -144,7 +145,7 @@ class IncludeRole(TaskInclude):
ir._from_files[from_key] = basename(args_value)
apply_attrs = ir.args.get('apply', {})
if apply_attrs and ir.action != 'include_role':
if apply_attrs and ir.action not in C._ACTION_INCLUDE_ROLE:
raise AnsibleParserError('Invalid options for %s: apply' % ir.action, obj=data)
elif not isinstance(apply_attrs, dict):
raise AnsibleParserError('Expected a dict for apply but got %s instead' % type(apply_attrs), obj=data)

@ -153,7 +153,7 @@ class Task(Base, Conditional, Taggable, CollectionSearch):
def __repr__(self):
''' returns a human readable representation of the task '''
if self.get_name() == 'meta':
if self.get_name() in C._ACTION_META:
return "TASK: meta (%s)" % self.args['_raw_params']
else:
return "TASK: %s" % self.get_name()
@ -231,7 +231,7 @@ class Task(Base, Conditional, Taggable, CollectionSearch):
# the command/shell/script modules used to support the `cmd` arg,
# which corresponds to what we now call _raw_params, so move that
# value over to _raw_params (assuming it is empty)
if action in ('command', 'shell', 'script'):
if action in C._ACTION_HAS_CMD:
if 'cmd' in args:
if args.get('_raw_params', '') != '':
raise AnsibleError("The 'cmd' argument cannot be used when other raw parameters are specified."
@ -263,7 +263,7 @@ class Task(Base, Conditional, Taggable, CollectionSearch):
# pre-2.0 syntax allowed variables for include statements at the top level of the task,
# so we move those into the 'vars' dictionary here, and show a deprecation message
# as we will remove this at some point in the future.
if action in ('include',) and k not in self._valid_attrs and k not in self.DEPRECATED_ATTRIBUTES:
if action in C._ACTION_INCLUDE and k not in self._valid_attrs and k not in self.DEPRECATED_ATTRIBUTES:
display.deprecated("Specifying include variables at the top-level of the task is deprecated."
" Please see:\nhttps://docs.ansible.com/ansible/playbooks_roles.html#task-include-files-and-encouraging-reuse\n\n"
" for currently supported syntax regarding included files and variables",
@ -327,7 +327,7 @@ class Task(Base, Conditional, Taggable, CollectionSearch):
env[k] = templar.template(v, convert_bare=False)
except AnsibleUndefinedVariable as e:
error = to_native(e)
if self.action in ('setup', 'gather_facts') and 'ansible_facts.env' in error or 'ansible_env' in error:
if self.action in C._ACTION_FACT_GATHERING and 'ansible_facts.env' in error or 'ansible_env' in error:
# ignore as fact gathering is required for 'env' facts
return
raise
@ -394,7 +394,7 @@ class Task(Base, Conditional, Taggable, CollectionSearch):
all_vars = dict()
if self._parent:
all_vars.update(self._parent.get_include_params())
if self.action in ('include', 'include_tasks', 'include_role'):
if self.action in C._ACTION_ALL_INCLUDES:
all_vars.update(self.vars)
return all_vars

@ -76,7 +76,7 @@ class TaskInclude(Task):
# validate bad args, otherwise we silently ignore
bad_opts = my_arg_names.difference(self.VALID_ARGS)
if bad_opts and task.action in ('include_tasks', 'import_tasks'):
if bad_opts and task.action in C._ACTION_ALL_PROPER_INCLUDE_IMPORT_TASKS:
raise AnsibleParserError('Invalid options for %s: %s' % (task.action, ','.join(list(bad_opts))), obj=data)
if not task.args.get('_raw_params'):
@ -85,7 +85,7 @@ class TaskInclude(Task):
raise AnsibleParserError('No file specified for %s' % task.action)
apply_attrs = task.args.get('apply', {})
if apply_attrs and task.action != 'include_tasks':
if apply_attrs and task.action not in C._ACTION_INCLUDE_TASKS:
raise AnsibleParserError('Invalid options for %s: apply' % task.action, obj=data)
elif not isinstance(apply_attrs, dict):
raise AnsibleParserError('Expected a dict for apply but got %s instead' % type(apply_attrs), obj=data)
@ -98,7 +98,7 @@ class TaskInclude(Task):
diff = set(ds.keys()).difference(self.VALID_INCLUDE_KEYWORDS)
for k in diff:
# This check doesn't handle ``include`` as we have no idea at this point if it is static or not
if ds[k] is not Sentinel and ds['action'] in ('include_tasks', 'include_role'):
if ds[k] is not Sentinel and ds['action'] in C._ACTION_ALL_INCLUDE_ROLE_TASKS:
if C.INVALID_TASK_ATTRIBUTE_FAILED:
raise AnsibleParserError("'%s' is not a valid attribute for a %s" % (k, self.__class__.__name__), obj=ds)
else:
@ -117,7 +117,7 @@ class TaskInclude(Task):
we need to include the args of the include into the vars as
they are params to the included tasks. But ONLY for 'include'
'''
if self.action != 'include':
if self.action not in C._ACTION_INCLUDE:
all_vars = super(TaskInclude, self).get_vars()
else:
all_vars = dict()

@ -248,7 +248,7 @@ class CallbackBase(AnsiblePlugin):
''' removes data from results for display '''
# mostly controls that debug only outputs what it was meant to
if task_name == 'debug':
if task_name in C._ACTION_DEBUG:
if 'msg' in result:
# msg should be alone
for key in list(result.keys()):

@ -675,7 +675,7 @@ class StrategyBase:
host_list = self.get_task_hosts(iterator, original_host, original_task)
if original_task.action == 'include_vars':
if original_task.action in C._ACTION_INCLUDE_VARS:
for (var_name, var_value) in iteritems(result_item['ansible_facts']):
# find the host we're actually referring too here, which may
# be a host that is not really in inventory at all
@ -689,9 +689,10 @@ class StrategyBase:
# we set BOTH fact and nonpersistent_facts (aka hostvar)
# when fact is retrieved from cache in subsequent operations it will have the lower precedence,
# but for playbook setting it the 'higher' precedence is kept
if original_task.action != 'set_fact' or cacheable:
is_set_fact = original_task.action in C._ACTION_SET_FACT
if not is_set_fact or cacheable:
self._variable_manager.set_host_facts(target_host, result_item['ansible_facts'].copy())
if original_task.action == 'set_fact':
if is_set_fact:
self._variable_manager.set_nonpersistent_facts(target_host, result_item['ansible_facts'].copy())
if 'ansible_stats' in result_item and 'data' in result_item['ansible_stats'] and result_item['ansible_stats']['data']:

@ -189,7 +189,7 @@ class StrategyModule(StrategyBase):
del self._blocked_hosts[host_name]
continue
if task.action == 'meta':
if task.action in C._ACTION_META:
self._execute_meta(task, play_context, iterator, target_host=host)
self._blocked_hosts[host_name] = False
else:

@ -31,6 +31,7 @@ DOCUMENTATION = '''
author: Ansible Core Team
'''
from ansible import constants as C
from ansible.errors import AnsibleError, AnsibleAssertionError
from ansible.executor.play_iterator import PlayIterator
from ansible.module_utils.six import iteritems
@ -271,7 +272,7 @@ class StrategyModule(StrategyBase):
# corresponding action plugin
action = None
if task.action == 'meta':
if task.action in C._ACTION_META:
# for the linear strategy, we run meta tasks just once and for
# all hosts currently being iterated over rather than one host
results.extend(self._execute_meta(task, play_context, iterator, host))
@ -409,7 +410,7 @@ class StrategyModule(StrategyBase):
for res in results:
# execute_meta() does not set 'failed' in the TaskResult
# so we skip checking it with the meta tasks and look just at the iterator
if (res.is_failed() or res._task.action == 'meta') and iterator.is_failed(res._host):
if (res.is_failed() or res._task.action in C._ACTION_META) and iterator.is_failed(res._host):
failed_hosts.append(res._host.name)
elif res.is_unreachable():
unreachable_hosts.append(res._host.name)

@ -0,0 +1,33 @@
# (c) 2020, Felix Fontein <felix@fontein.de>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
def add_internal_fqcns(names):
'''
Given a sequence of action/module names, returns a list of these names
with the same names with the prefixes `ansible.builtin.` and
`ansible.legacy.` added for all names that are not already FQCNs.
'''
result = []
for name in names:
result.append(name)
if '.' not in name:
result.append('ansible.builtin.%s' % name)
result.append('ansible.legacy.%s' % name)
return result

@ -219,7 +219,7 @@ class VariableManager:
# if we have a task in this context, and that task has a role, make
# sure it sees its defaults above any other roles, as we previously
# (v1) made sure each task had a copy of its roles default vars
if task._role is not None and (play or task.action == 'include_role'):
if task._role is not None and (play or task.action in C._ACTION_INCLUDE_ROLE):
all_vars = _combine_and_track(all_vars, task._role.get_default_vars(dep_chain=task.get_dep_chain()),
"role '%s' defaults" % task._role.name)

@ -0,0 +1,6 @@
- hosts: localhost
gather_facts: no
tasks:
- name: test item being present in the output
ansible.builtin.debug: var=item
loop: [1, 2, 3]

@ -9,3 +9,9 @@ for i in 1 2 3; do
grep "ok: \[localhost\] => (item=$i)" out
grep "\"item\": $i" out
done
ansible-playbook main_fqcn.yml -i ../../inventory | tee out
for i in 1 2 3; do
grep "ok: \[localhost\] => (item=$i)" out
grep "\"item\": $i" out
done

@ -49,23 +49,29 @@ test "$(grep -E -c 'Expected a string for vars_from but got' test_include_role_v
## Max Recursion Depth
# https://github.com/ansible/ansible/issues/23609
ANSIBLE_STRATEGY='linear' ansible-playbook test_role_recursion.yml -i inventory "$@"
ANSIBLE_STRATEGY='linear' ansible-playbook test_role_recursion_fqcn.yml -i inventory "$@"
## Nested tasks
# https://github.com/ansible/ansible/issues/34782
ANSIBLE_STRATEGY='linear' ansible-playbook test_nested_tasks.yml -i inventory "$@"
ANSIBLE_STRATEGY='linear' ansible-playbook test_nested_tasks_fqcn.yml -i inventory "$@"
ANSIBLE_STRATEGY='free' ansible-playbook test_nested_tasks.yml -i inventory "$@"
ANSIBLE_STRATEGY='free' ansible-playbook test_nested_tasks_fqcn.yml -i inventory "$@"
## Tons of top level include_tasks
# https://github.com/ansible/ansible/issues/36053
# Fixed by https://github.com/ansible/ansible/pull/36075
gen_task_files
ANSIBLE_STRATEGY='linear' ansible-playbook test_copious_include_tasks.yml -i inventory "$@"
ANSIBLE_STRATEGY='linear' ansible-playbook test_copious_include_tasks_fqcn.yml -i inventory "$@"
ANSIBLE_STRATEGY='free' ansible-playbook test_copious_include_tasks.yml -i inventory "$@"
ANSIBLE_STRATEGY='free' ansible-playbook test_copious_include_tasks_fqcn.yml -i inventory "$@"
rm -f tasks/hello/*.yml
# Inlcuded tasks should inherit attrs from non-dynamic blocks in parent chain
# https://github.com/ansible/ansible/pull/38827
ANSIBLE_STRATEGY='linear' ansible-playbook test_grandparent_inheritance.yml -i inventory "$@"
ANSIBLE_STRATEGY='linear' ansible-playbook test_grandparent_inheritance_fqcn.yml -i inventory "$@"
# undefined_var
ANSIBLE_STRATEGY='linear' ansible-playbook undefined_var/playbook.yml -i inventory "$@"
@ -119,3 +125,4 @@ test "$(grep -c 'ok=3' test_allow_single_role_dup.out)" = 1
ANSIBLE_HOST_PATTERN_MISMATCH=error ansible-playbook empty_group_warning/playbook.yml
ansible-playbook test_include_loop.yml "$@"
ansible-playbook test_include_loop_fqcn.yml "$@"

@ -0,0 +1,44 @@
- name: Test many ansible.builtin.include_tasks
hosts: testhost
gather_facts: no
tasks:
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-001.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-002.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-003.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-004.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-005.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-006.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-007.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-008.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-009.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-010.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-011.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-012.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-013.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-014.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-015.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-016.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-017.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-018.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-019.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-020.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-021.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-022.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-023.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-024.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-025.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-026.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-027.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-028.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-029.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-030.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-031.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-032.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-033.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-034.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-035.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-036.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-037.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-038.yml"
- ansible.builtin.include_tasks: "{{ playbook_dir }}/tasks/hello/tasks-file-039.yml"

@ -0,0 +1,29 @@
---
- hosts: testhost
gather_facts: false
tasks:
- debug:
var: inventory_hostname
- name: Test included tasks inherit from block
check_mode: true
block:
- ansible.builtin.include_tasks: grandchild/block_include_tasks.yml
- debug:
var: block_include_result
- assert:
that:
- block_include_result is skipped
- name: Test included tasks inherit deeply from import
ansible.builtin.import_tasks: grandchild/import.yml
check_mode: true
- debug:
var: import_include_include_result
- assert:
that:
- import_include_include_result is skipped

@ -0,0 +1,17 @@
- hosts: localhost
gather_facts: false
tasks:
- name: skipped include undefined loop
ansible.builtin.include_tasks: doesnt_matter.yml
loop: '{{ lkjsdflkjsdlfkjsdlfkjsdf }}'
when: false
register: skipped_include
- debug:
var: skipped_include
- assert:
that:
- skipped_include.results is undefined
- skipped_include.skip_reason is defined
- skipped_include is skipped

@ -0,0 +1,6 @@
- name: >-
verify that multiple level of nested statements and
include+meta doesnt mess included files mecanisms
hosts: testhost
tasks:
- ansible.builtin.include_tasks: ./tasks/nested/nested.yml

@ -0,0 +1,7 @@
- name: Test max recursion depth
hosts: testhost
tasks:
- ansible.builtin.import_role:
name: role1
tasks_from: r1t01.yml

@ -65,12 +65,12 @@
- "testing == 456"
- "base_dir == 'services'"
- "webapp_containers == 10"
- "{{ include_every_dir.ansible_included_var_files | length }} == 6"
- "{{ include_every_dir.ansible_included_var_files | length }} == 7"
- "'vars/all/all.yml' in include_every_dir.ansible_included_var_files[0]"
- "'vars/environments/development/all.yml' in include_every_dir.ansible_included_var_files[1]"
- "'vars/environments/development/services/webapp.yml' in include_every_dir.ansible_included_var_files[2]"
- "'vars/services/webapp.yml' in include_every_dir.ansible_included_var_files[4]"
- "'vars/webapp/file_without_extension' in include_every_dir.ansible_included_var_files[5]"
- "'vars/services/webapp.yml' in include_every_dir.ansible_included_var_files[5]"
- "'vars/webapp/file_without_extension' in include_every_dir.ansible_included_var_files[6]"
- name: include every directory in vars except files matching webapp.yml
include_vars:
@ -85,7 +85,7 @@
that:
- "testing == 789"
- "base_dir == 'environments/development'"
- "{{ include_without_webapp.ansible_included_var_files | length }} == 3"
- "{{ include_without_webapp.ansible_included_var_files | length }} == 4"
- "'webapp.yml' not in '{{ include_without_webapp.ansible_included_var_files | join(' ') }}'"
- "'file_without_extension' not in '{{ include_without_webapp.ansible_included_var_files | join(' ') }}'"
@ -152,3 +152,13 @@
assert:
that:
- "'Could not find file' in include_with_non_existent_file.message"
- name: include var (FQCN) with raw params
ansible.builtin.include_vars: >
services/service_vars_fqcn.yml
- name: Verify that FQCN of include_vars works
assert:
that:
- "'my_custom_service' == service_name_fqcn"
- "'my_custom_service' == service_name_tmpl_fqcn"

@ -0,0 +1,3 @@
---
service_name_fqcn: 'my_custom_service'
service_name_tmpl_fqcn: '{{ service_name_fqcn }}'

@ -4,3 +4,6 @@
- include: inner.yml
with_items:
- '1'
- ansible.builtin.include: inner_fqcn.yml
with_items:
- '1'

@ -7,3 +7,4 @@
- assert:
that:
- "inner == 'reached'"
- "inner_fqcn == 'reached'"

@ -11,6 +11,14 @@ for test_strategy in linear free; do
grep -q '"skip_reason": "end_host conditional evaluated to False, continuing execution for testhost"' <<< "$out"
grep -q "play not ended for testhost" <<< "$out"
grep -qv "play not ended for testhost2" <<< "$out"
out="$(ansible-playbook test_end_host_fqcn.yml -i inventory.yml -e test_strategy=$test_strategy -vv "$@")"
grep -q "META: end_host conditional evaluated to false, continuing execution for testhost" <<< "$out"
grep -q "META: ending play for testhost2" <<< "$out"
grep -q '"skip_reason": "end_host conditional evaluated to False, continuing execution for testhost"' <<< "$out"
grep -q "play not ended for testhost" <<< "$out"
grep -qv "play not ended for testhost2" <<< "$out"
done
# test end_host meta task, on all hosts
@ -21,6 +29,13 @@ for test_strategy in linear free; do
grep -q "META: ending play for testhost2" <<< "$out"
grep -qv "play not ended for testhost" <<< "$out"
grep -qv "play not ended for testhost2" <<< "$out"
out="$(ansible-playbook test_end_host_all_fqcn.yml -i inventory.yml -e test_strategy=$test_strategy -vv "$@")"
grep -q "META: ending play for testhost" <<< "$out"
grep -q "META: ending play for testhost2" <<< "$out"
grep -qv "play not ended for testhost" <<< "$out"
grep -qv "play not ended for testhost2" <<< "$out"
done
# test end_play meta task
@ -29,4 +44,9 @@ for test_strategy in linear free; do
grep -q "META: ending play" <<< "$out"
grep -qv 'Failed to end using end_play' <<< "$out"
out="$(ansible-playbook test_end_play_fqcn.yml -i inventory.yml -e test_strategy=$test_strategy -vv "$@")"
grep -q "META: ending play" <<< "$out"
grep -qv 'Failed to end using end_play' <<< "$out"
done

@ -0,0 +1,13 @@
- name: "Testing end_host all hosts with strategy={{ test_strategy | default('linear') }}"
hosts:
- testhost
- testhost2
gather_facts: no
strategy: "{{ test_strategy | default('linear') }}"
tasks:
- debug:
- ansible.builtin.meta: end_host
- debug:
msg: "play not ended {{ inventory_hostname }}"

@ -0,0 +1,14 @@
- name: "Testing end_host with strategy={{ test_strategy | default('linear') }}"
hosts:
- testhost
- testhost2
gather_facts: no
strategy: "{{ test_strategy | default('linear') }}"
tasks:
- debug:
- ansible.builtin.meta: end_host
when: "host_var_role_name == 'role2'" # end play for testhost2, see inventory
- debug:
msg: "play not ended for {{ inventory_hostname }}"

@ -0,0 +1,12 @@
- name: Testing end_play with strategy {{ test_strategy | default('linear') }}
hosts: testhost:testhost2
gather_facts: no
strategy: "{{ test_strategy | default('linear') }}"
tasks:
- debug:
msg: "Testing end_play on host {{ inventory_hostname }}"
- ansible.builtin.meta: end_play
- fail:
msg: 'Failed to end using end_play'

@ -17,3 +17,19 @@
assert:
that:
- dig_list == ['one', 'two', 'three', 'four']
- name: Generate inline loop for set_fact (FQCN)
ansible.builtin.set_fact:
dig_list_fqcn: "{{ dig_list_fqcn + [ item ] }}"
loop:
- two
- three
- four
vars:
dig_list_fqcn:
- one
- name: verify cumulative set fact worked (FQCN)
assert:
that:
- dig_list_fqcn == ['one', 'two', 'three', 'four']

@ -5,3 +5,6 @@
- name: set ssh jump host args
set_fact:
ansible_ssh_common_args: "-o ProxyCommand='ssh -W %h:%p -q root@localhost'"
- name: set ssh jump host args (FQCN)
ansible.builtin.set_fact:
ansible_ssh_common_args: "-o ProxyCommand='ssh -W %h:%p -q root@localhost'"

@ -18,3 +18,18 @@
- this_is_also_string == False
- this_is_another_string == False
- this_is_more_strings == False
- ansible.builtin.set_fact:
this_is_string_fqcn: "yes"
this_is_not_string_fqcn: yes
this_is_also_string_fqcn: "{{ string_var }}"
this_is_another_string_fqcn: !!str "{% set thing = '' + string_var + '' %}{{ thing }}"
this_is_more_strings_fqcn: '{{ string_var + "" }}'
- assert:
that:
- this_is_string_fqcn == True
- this_is_not_string_fqcn == True
- this_is_also_string_fqcn == False
- this_is_another_string_fqcn == False
- this_is_more_strings_fqcn == False

@ -18,3 +18,18 @@
- this_is_also_string == 'no'
- this_is_another_string == 'no'
- this_is_more_strings == 'no'
- ansible.builtin.set_fact:
this_is_string_fqcn: "yes"
this_is_not_string_fqcn: yes
this_is_also_string_fqcn: "{{ string_var }}"
this_is_another_string_fqcn: !!str "{% set thing = '' + string_var + '' %}{{ thing }}"
this_is_more_strings_fqcn: '{{ string_var + "" }}'
- assert:
that:
- this_is_string_fqcn == 'yes'
- this_is_not_string_fqcn == True
- this_is_also_string_fqcn == 'no'
- this_is_another_string_fqcn == 'no'
- this_is_more_strings_fqcn == 'no'

@ -45,6 +45,49 @@
that:
- fact_not_cached == 'this_should_not_be_cached!'
- name: show foobar fact before (FQCN)
debug:
var: ansible_foobar_fqcn
- name: set a persistent fact foobar (FQCN)
set_fact:
ansible_foobar_fqcn: 'foobar_fqcn_from_set_fact_cacheable'
cacheable: true
- name: show foobar fact after (FQCN)
debug:
var: ansible_foobar_fqcn
- name: assert ansible_foobar_fqcn is correct value (FQCN)
assert:
that:
- ansible_foobar_fqcn == 'foobar_fqcn_from_set_fact_cacheable'
- name: set a non persistent fact that will not be cached (FQCN)
set_fact:
ansible_foobar_not_cached_fqcn: 'this_should_not_be_cached'
- name: show ansible_foobar_not_cached_fqcn fact after being set (FQCN)
debug:
var: ansible_foobar_not_cached_fqcn
- name: assert ansible_foobar_not_cached_fqcn is correct value (FQCN)
assert:
that:
- ansible_foobar_not_cached_fqcn == 'this_should_not_be_cached'
- name: set another non persistent fact that will not be cached (FQCN)
set_fact: "cacheable=no fact_not_cached_fqcn='this_should_not_be_cached!'"
- name: show fact_not_cached_fqcn fact after being set (FQCN)
debug:
var: fact_not_cached_fqcn
- name: assert fact_not_cached_fqcn is correct value (FQCN)
assert:
that:
- fact_not_cached_fqcn == 'this_should_not_be_cached!'
- name: the second play
hosts: localhost
tasks:
@ -57,26 +100,43 @@
that:
- ansible_foobar == 'foobar_from_set_fact_cacheable'
- name: show ansible_nodename
- name: show foobar fact after second play (FQCN)
debug:
var: ansible_foobar_fqcn
- name: assert ansible_foobar is correct value (FQCN)
assert:
that:
- ansible_foobar_fqcn == 'foobar_fqcn_from_set_fact_cacheable'
- name: show ansible_nodename and ansible_os_family
hosts: localhost
tasks:
- name: show nodename fact after second play
debug:
var: ansible_nodename
- name: show os_family fact after second play (FQCN)
debug:
var: ansible_os_family
- name: show ansible_nodename overridden with var
- name: show ansible_nodename and ansible_os_family overridden with var
hosts: localhost
vars:
ansible_nodename: 'nodename_from_play_vars'
ansible_os_family: 'os_family_from_play_vars'
tasks:
- name: show nodename fact after second play
debug:
var: ansible_nodename
- name: show os_family fact after second play (FQCN)
debug:
var: ansible_os_family
- name: verify ansible_nodename from vars overrides the fact
hosts: localhost
vars:
ansible_nodename: 'nodename_from_play_vars'
ansible_os_family: 'os_family_from_play_vars'
tasks:
- name: show nodename fact
debug:
@ -87,7 +147,16 @@
that:
- ansible_nodename == 'nodename_from_play_vars'
- name: set_fact ansible_nodename
- name: show os_family fact (FQCN)
debug:
var: ansible_os_family
- name: assert ansible_os_family is correct value (FQCN)
assert:
that:
- ansible_os_family == 'os_family_from_play_vars'
- name: set_fact ansible_nodename and ansible_os_family
hosts: localhost
tasks:
- name: set a persistent fact nodename
@ -103,10 +172,24 @@
that:
- ansible_nodename == 'nodename_from_set_fact_cacheable'
- name: verify that set_fact ansible_nodename non_cacheable overrides ansible_nodename in vars
- name: set a persistent fact os_family (FQCN)
ansible.builtin.set_fact:
ansible_os_family: 'os_family_from_set_fact_cacheable'
- name: show os_family fact (FQCN)
debug:
var: ansible_os_family
- name: assert ansible_os_family is correct value (FQCN)
assert:
that:
- ansible_os_family == 'os_family_from_set_fact_cacheable'
- name: verify that set_fact ansible_xxx non_cacheable overrides ansible_xxx in vars
hosts: localhost
vars:
ansible_nodename: 'nodename_from_play_vars'
ansible_os_family: 'os_family_from_play_vars'
tasks:
- name: show nodename fact
debug:
@ -117,10 +200,20 @@
that:
- ansible_nodename == 'nodename_from_set_fact_cacheable'
- name: verify that set_fact_cacheable in previous play overrides ansible_nodename in vars
- name: show os_family fact (FQCN)
debug:
var: ansible_os_family
- name: assert ansible_os_family is correct value (FQCN)
assert:
that:
- ansible_os_family == 'os_family_from_set_fact_cacheable'
- name: verify that set_fact_cacheable in previous play overrides ansible_xxx in vars
hosts: localhost
vars:
ansible_nodename: 'nodename_from_play_vars'
ansible_os_family: 'os_family_from_play_vars'
tasks:
- name: show nodename fact
debug:
@ -131,7 +224,16 @@
that:
- ansible_nodename == 'nodename_from_set_fact_cacheable'
- name: set_fact ansible_nodename cacheable
- name: show os_family fact (FQCN)
debug:
var: ansible_os_family
- name: assert ansible_os_family is correct value (FQCN)
assert:
that:
- ansible_os_family == 'os_family_from_set_fact_cacheable'
- name: set_fact ansible_nodename and ansible_os_family cacheable
hosts: localhost
tasks:
- name: set a persistent fact nodename
@ -148,11 +250,26 @@
that:
- ansible_nodename == 'nodename_from_set_fact_cacheable'
- name: set a persistent fact os_family (FQCN)
ansible.builtin.set_fact:
ansible_os_family: 'os_family_from_set_fact_cacheable'
cacheable: true
- name: show os_family fact (FQCN)
debug:
var: ansible_os_family
- name: verify that set_fact_cacheable in previous play overrides ansible_nodename in vars
- name: assert ansible_os_family is correct value (FQCN)
assert:
that:
- ansible_os_family == 'os_family_from_set_fact_cacheable'
- name: verify that set_fact_cacheable in previous play overrides ansible_xxx in vars
hosts: localhost
vars:
ansible_nodename: 'nodename_from_play_vars'
ansible_os_family: 'os_family_from_play_vars'
tasks:
- name: show nodename fact
debug:
@ -163,10 +280,20 @@
that:
- ansible_nodename == 'nodename_from_set_fact_cacheable'
- name: show os_family fact (FQCN)
debug:
var: ansible_os_family
- name: assert ansible_os_family is correct value (FQCN)
assert:
that:
- ansible_os_family == 'os_family_from_set_fact_cacheable'
- name: the fourth play
hosts: localhost
vars:
ansible_foobar: 'foobar_from_play_vars'
ansible_foobar_fqcn: 'foobar_fqcn_from_play_vars'
tasks:
- name: show example fact
debug:
@ -181,3 +308,17 @@
assert:
that:
- ansible_example == 'foobar_from_set_fact_cacheable'
- name: show example fact (FQCN)
debug:
var: ansible_example_fqcn
- name: set a persistent fact example (FQCN)
set_fact:
ansible_example_fqcn: 'foobar_fqcn_from_set_fact_cacheable'
cacheable: true
- name: assert ansible_example_fqcn is correct value (FQCN)
assert:
that:
- ansible_example_fqcn == 'foobar_fqcn_from_set_fact_cacheable'

@ -28,3 +28,30 @@
assert:
that:
- fact_not_cached is undefined
- name: show ansible_foobar_fqcn fact (FQCN)
debug:
var: ansible_foobar_fqcn
- name: assert ansible_foobar_fqcn is correct value when read from cache (FQCN)
assert:
that:
- ansible_foobar_fqcn == 'foobar_fqcn_from_set_fact_cacheable'
- name: show ansible_foobar_fqcn_not_cached fact (FQCN)
debug:
var: ansible_foobar_fqcn_not_cached
- name: assert ansible_foobar_fqcn_not_cached is not cached (FQCN)
assert:
that:
- ansible_foobar_fqcn_not_cached is undefined
- name: show fact_not_cached_fqcn fact (FQCN)
debug:
var: fact_not_cached_fqcn
- name: assert fact_not_cached_fqcn is not cached (FQCN)
assert:
that:
- fact_not_cached_fqcn is undefined

@ -19,3 +19,21 @@
assert:
that:
- ansible_foobar_not_cached is undefined
- name: show ansible_foobar fact (FQCN)
debug:
var: ansible_foobar_fqcn
- name: assert ansible_foobar is correct value (FQCN)
assert:
that:
- ansible_foobar_fqcn is undefined
- name: show ansible_foobar_not_cached fact (FQCN)
debug:
var: ansible_foobar_fqcn_not_cached
- name: assert ansible_foobar_not_cached is not cached (FQCN)
assert:
that:
- ansible_foobar_fqcn_not_cached is undefined

Loading…
Cancel
Save