From dd8d699981249038b6d9d8dda05ea57239a7d2d4 Mon Sep 17 00:00:00 2001 From: Brian Coca Date: Wed, 15 Mar 2017 20:12:16 -0400 Subject: [PATCH] namespace facts (#18445) * namespace facts always namespace facts, make the polluting of 'main' conditional on config * updated to 2.4 * Update intro_configuration.rst --- docs/docsite/rst/intro_configuration.rst | 21 +++++++++++++++++++++ examples/ansible.cfg | 4 ++++ lib/ansible/constants.py | 1 + lib/ansible/executor/task_executor.py | 8 ++++++-- lib/ansible/plugins/action/__init__.py | 2 +- lib/ansible/vars/__init__.py | 6 +++++- 6 files changed, 38 insertions(+), 4 deletions(-) diff --git a/docs/docsite/rst/intro_configuration.rst b/docs/docsite/rst/intro_configuration.rst index d44a5881e80eb7..c0df1bd0eaf886 100644 --- a/docs/docsite/rst/intro_configuration.rst +++ b/docs/docsite/rst/intro_configuration.rst @@ -762,6 +762,27 @@ always default to the current user if this is not defined:: remote_user = root + +.. _restrict_facts_namespace: + +restrict_facts_namespace +======================== + +.. versionadded:: 2.4 + +This allows restricting facts in their own namespace (under ansible_facts) instead of pushing them into the main. +False by default. Can also be set via the environment variable `ANSIBLE_RESTRICT_FACTS`. Using `ansible_system` as an example: + +When False:: + + - debug: var=ansible_system + + +When True:: + + - debug: var=ansible_facts.ansible_system + + .. _retry_files_enabled: retry_files_enabled diff --git a/examples/ansible.cfg b/examples/ansible.cfg index 109f508002ed91..459fc9719c47db 100644 --- a/examples/ansible.cfg +++ b/examples/ansible.cfg @@ -288,6 +288,10 @@ # only update this setting if you know how this works, otherwise it can break module execution #network_group_modules=['eos', 'nxos', 'ios', 'iosxr', 'junos', 'vyos'] +# This keeps facts from polluting the main namespace as variables. +# Setting to True keeps them under the ansible_facts namespace, the default is False +#restrict_facts_namespace: True + [privilege_escalation] #become=True #become_method=sudo diff --git a/lib/ansible/constants.py b/lib/ansible/constants.py index 1f00afcdcb2b2f..f79f9edcbba594 100644 --- a/lib/ansible/constants.py +++ b/lib/ansible/constants.py @@ -236,6 +236,7 @@ def load_config_file(): DEFAULT_INTERNAL_POLL_INTERVAL = get_config(p, DEFAULTS, 'internal_poll_interval', None, 0.001, value_type='float') ERROR_ON_MISSING_HANDLER = get_config(p, DEFAULTS, 'error_on_missing_handler', 'ANSIBLE_ERROR_ON_MISSING_HANDLER', True, value_type='boolean') SHOW_CUSTOM_STATS = get_config(p, DEFAULTS, 'show_custom_stats', 'ANSIBLE_SHOW_CUSTOM_STATS', False, value_type='boolean') +NAMESPACE_FACTS = get_config(p, DEFAULTS, 'restrict_facts_namespace', 'ANSIBLE_RESTRICT_FACTS', False, value_type='boolean') # static includes DEFAULT_TASK_INCLUDES_STATIC = get_config(p, DEFAULTS, 'task_includes_static', 'ANSIBLE_TASK_INCLUDES_STATIC', False, value_type='boolean') diff --git a/lib/ansible/executor/task_executor.py b/lib/ansible/executor/task_executor.py index c2c87ef5a81ce6..64f8a6c3e1065b 100644 --- a/lib/ansible/executor/task_executor.py +++ b/lib/ansible/executor/task_executor.py @@ -557,7 +557,9 @@ def _evaluate_failed_when_result(result): return failed_when_result if 'ansible_facts' in result: - vars_copy.update(result['ansible_facts']) + if not C.NAMESPACE_FACTS: + vars_copy.update(result['ansible_facts']) + vars_copy.update({'ansible_facts': result['ansible_facts']}) # set the failed property if the result has a non-zero rc. This will be # overridden below if the failed_when property is set @@ -596,7 +598,9 @@ def _evaluate_failed_when_result(result): variables[self._task.register] = wrap_var(result) if 'ansible_facts' in result: - variables.update(result['ansible_facts']) + if not C.NAMESPACE_FACTS: + variables.update(result['ansible_facts']) + variables.update({'ansible_facts': result['ansible_facts']}) # save the notification target in the result, if it was specified, as # this task may be running in a loop in which case the notification diff --git a/lib/ansible/plugins/action/__init__.py b/lib/ansible/plugins/action/__init__.py index 736a9cab02633c..95343c2e696bf1 100644 --- a/lib/ansible/plugins/action/__init__.py +++ b/lib/ansible/plugins/action/__init__.py @@ -733,7 +733,7 @@ def _execute_module(self, module_name=None, module_args=None, tmp=None, task_var # actually execute res = self._low_level_execute_command(cmd, sudoable=sudoable, in_data=in_data) - # parse the main result, also cleans up internal keys + # parse the main result data = self._parse_returned_data(res) #NOTE: INTERNAL KEYS ONLY ACCESSIBLE HERE diff --git a/lib/ansible/vars/__init__.py b/lib/ansible/vars/__init__.py index 5afdfd14ee6056..03f435d64abba7 100644 --- a/lib/ansible/vars/__init__.py +++ b/lib/ansible/vars/__init__.py @@ -281,7 +281,11 @@ def get_vars(self, loader, play=None, host=None, task=None, include_hostvars=Tru # finally, the facts caches for this host, if it exists try: host_facts = wrap_var(self._fact_cache.get(host.name, dict())) - all_vars = combine_vars(all_vars, host_facts) + if not C.NAMESPACE_FACTS: + # allow facts to polute main namespace + all_vars = combine_vars(all_vars, host_facts) + # always return namespaced facts + all_vars = combine_vars(all_vars, {'ansible_facts': host_facts}) except KeyError: pass