summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/ansible/config/base.yml11
-rw-r--r--lib/ansible/executor/play_iterator.py4
-rw-r--r--lib/ansible/modules/system/gather_facts.py39
-rw-r--r--lib/ansible/plugins/action/gather_facts.py41
-rw-r--r--lib/ansible/plugins/loader.py2
-rw-r--r--test/integration/targets/gathering/aliases1
-rw-r--r--test/integration/targets/gathering/explicit.yml14
-rw-r--r--test/integration/targets/gathering/implicit.yml23
-rwxr-xr-xtest/integration/targets/gathering/runme.sh7
-rw-r--r--test/integration/targets/gathering/smart.yml23
-rw-r--r--test/integration/targets/gathering/uuid.fact10
-rw-r--r--test/integration/targets/pull/pull-integration-test/local.yml1
12 files changed, 173 insertions, 3 deletions
diff --git a/lib/ansible/config/base.yml b/lib/ansible/config/base.yml
index 27f1e14250..3de070eb36 100644
--- a/lib/ansible/config/base.yml
+++ b/lib/ansible/config/base.yml
@@ -1284,6 +1284,17 @@ ERROR_ON_MISSING_HANDLER:
ini:
- {key: error_on_missing_handler, section: defaults}
type: boolean
+FACTS_MODULES:
+ name: Gather Facts Modules
+ default:
+ - setup
+ description: "Which modules to run during a play's fact gathering stage"
+ env: [{name: ANSIBLE_FACTS_MODULES}]
+ ini:
+ - {key: facts_modules, section: defaults}
+ type: list
+ vars:
+ - name: ansible_facts_modules
GALAXY_IGNORE_CERTS:
name: Galaxy validate certs
default: False
diff --git a/lib/ansible/executor/play_iterator.py b/lib/ansible/executor/play_iterator.py
index aaaa59697b..e312908400 100644
--- a/lib/ansible/executor/play_iterator.py
+++ b/lib/ansible/executor/play_iterator.py
@@ -173,7 +173,7 @@ class PlayIterator:
# the others.
setup_block.run_once = False
setup_task = Task(block=setup_block)
- setup_task.action = 'setup'
+ setup_task.action = 'gather_facts'
setup_task.name = 'Gathering Facts'
setup_task.tags = ['always']
setup_task.args = {
@@ -301,7 +301,7 @@ class PlayIterator:
if (gathering == 'implicit' and implied) or \
(gathering == 'explicit' and boolean(self._play.gather_facts, strict=False)) or \
- (gathering == 'smart' and implied and not (self._variable_manager._fact_cache.get(host.name, {}).get('module_setup', False))):
+ (gathering == 'smart' and implied and not (self._variable_manager._fact_cache.get(host.name, {}).get('_ansible_facts_gathered', False))):
# The setup block is always self._blocks[0], as we inject it
# during the play compilation in __init__ above.
setup_block = self._blocks[0]
diff --git a/lib/ansible/modules/system/gather_facts.py b/lib/ansible/modules/system/gather_facts.py
new file mode 100644
index 0000000000..8599334cf5
--- /dev/null
+++ b/lib/ansible/modules/system/gather_facts.py
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017 Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+ANSIBLE_METADATA = {'metadata_version': '1.1',
+ 'status': ['preview'],
+ 'supported_by': 'core'}
+
+
+DOCUMENTATION = '''
+---
+module: gather_facts
+version_added: 2.6
+short_description: Gathers facts about remote hosts
+description:
+ - This module takes care of executing the configured facts modules, the default is to use the M(setup) module.
+ - This module is automatically called by playbooks to gather useful variables about remote hosts that can be used in playbooks.
+ - It can also be executed directly by C(/usr/bin/ansible) to check what variables are available to a host.
+ - Ansible provides many I(facts) about the system, automatically.
+notes:
+ - This module is mostly a wrapper around other fact gathering modules.
+ - Options passed to this module must be supported by all the underlying fact modules configured.
+author:
+ - "Ansible Core Team"
+'''
+
+RETURN = """
+# depends on the fact module called
+"""
+
+EXAMPLES = """
+# Display facts from all hosts and store them indexed by I(hostname) at C(/tmp/facts).
+# ansible all -m gather_facts --tree /tmp/facts
+"""
diff --git a/lib/ansible/plugins/action/gather_facts.py b/lib/ansible/plugins/action/gather_facts.py
new file mode 100644
index 0000000000..cdaf5c5021
--- /dev/null
+++ b/lib/ansible/plugins/action/gather_facts.py
@@ -0,0 +1,41 @@
+# Copyright (c) 2017 Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+from collections import MutableMapping
+
+from ansible import constants as C
+from ansible.plugins.action import ActionBase
+
+
+class ActionModule(ActionBase):
+
+ def run(self, tmp=None, task_vars=None):
+ ''' handler for package operations '''
+
+ self._supports_check_mode = True
+ self._supports_async = True
+
+ result = super(ActionModule, self).run(tmp, task_vars)
+ result['ansible_facts'] = {}
+
+ for fact_module in C.config.get_config_value('FACTS_MODULES', variables=task_vars):
+
+ mod_args = task_vars.get('ansible_facts_modules', {}).get(fact_module, {})
+ if isinstance(mod_args, MutableMapping):
+ mod_args.update(self._task.args.copy())
+ else:
+ mod_args = self._task.args.copy()
+
+ if fact_module != 'setup':
+ del mod_args['gather_subset']
+
+ self._display.vvvv("Running %s" % fact_module)
+ result.update(self._execute_module(module_name=fact_module, module_args=mod_args, task_vars=task_vars, wrap_async=self._task.async_val))
+
+ # tell executor facts were gathered
+ result['ansible_facts']['_ansible_facts_gathered'] = True
+
+ return result
diff --git a/lib/ansible/plugins/loader.py b/lib/ansible/plugins/loader.py
index 4332a5a862..f387f8ace7 100644
--- a/lib/ansible/plugins/loader.py
+++ b/lib/ansible/plugins/loader.py
@@ -328,7 +328,7 @@ class PluginLoader:
from ansible.vars.reserved import is_reserved_name
plugin = self._find_plugin(name, mod_type=mod_type, ignore_deprecated=ignore_deprecated, check_aliases=check_aliases)
- if plugin and self.package == 'ansible.modules' and is_reserved_name(name):
+ if plugin and self.package == 'ansible.modules' and name not in ('gather_facts',) and is_reserved_name(name):
raise AnsibleError(
'Module "%s" shadows the name of a reserved keyword. Please rename or remove this module. Found at %s' % (name, plugin)
)
diff --git a/test/integration/targets/gathering/aliases b/test/integration/targets/gathering/aliases
new file mode 100644
index 0000000000..79d8b9285e
--- /dev/null
+++ b/test/integration/targets/gathering/aliases
@@ -0,0 +1 @@
+posix/ci/group3
diff --git a/test/integration/targets/gathering/explicit.yml b/test/integration/targets/gathering/explicit.yml
new file mode 100644
index 0000000000..453dfb6aa0
--- /dev/null
+++ b/test/integration/targets/gathering/explicit.yml
@@ -0,0 +1,14 @@
+- hosts: testhost
+ tasks:
+ - name: ensure facts have not been collected
+ assert:
+ that:
+ - ansible_facts is undefined or not 'fqdn' in ansible_facts
+
+- hosts: testhost
+ gather_facts: True
+ tasks:
+ - name: ensure facts have been collected
+ assert:
+ that:
+ - ansible_facts is defined and 'fqdn' in ansible_facts
diff --git a/test/integration/targets/gathering/implicit.yml b/test/integration/targets/gathering/implicit.yml
new file mode 100644
index 0000000000..f1ea965d32
--- /dev/null
+++ b/test/integration/targets/gathering/implicit.yml
@@ -0,0 +1,23 @@
+- hosts: testhost
+ tasks:
+ - name: check that facts were gathered but no local facts exist
+ assert:
+ that:
+ - ansible_facts is defined and 'fqdn' in ansible_facts
+ - not 'uuid' in ansible_local
+ - name: create 'local facts' for next gathering
+ copy:
+ src: uuid.fact
+ dest: /etc/ansible/facts.d/
+ mode: 0755
+
+- hosts: testhost
+ tasks:
+ - name: ensure facts are gathered and includes the new 'local facts' created above
+ assert:
+ that:
+ - ansible_facts is defined and 'fqdn' in ansible_facts
+ - "'uuid' in ansible_local"
+
+ - name: cleanup 'local facts' from target
+ file: path=/etc/ansible/facts.d/uuid.fact state=absent
diff --git a/test/integration/targets/gathering/runme.sh b/test/integration/targets/gathering/runme.sh
new file mode 100755
index 0000000000..1c0832c5a9
--- /dev/null
+++ b/test/integration/targets/gathering/runme.sh
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+set -eux
+
+ANSIBLE_GATHERING=smart ansible-playbook smart.yml --flush-cache -i ../../inventory -v "$@"
+ANSIBLE_GATHERING=implicit ansible-playbook implicit.yml --flush-cache -i ../../inventory -v "$@"
+ANSIBLE_GATHERING=explicit ansible-playbook explicit.yml --flush-cache -i ../../inventory -v "$@"
diff --git a/test/integration/targets/gathering/smart.yml b/test/integration/targets/gathering/smart.yml
new file mode 100644
index 0000000000..735cb461be
--- /dev/null
+++ b/test/integration/targets/gathering/smart.yml
@@ -0,0 +1,23 @@
+- hosts: testhost
+ tasks:
+ - name: ensure facts are gathered but no local exists
+ assert:
+ that:
+ - ansible_facts is defined and 'fqdn' in ansible_facts
+ - not 'uuid' in ansible_local
+ - name: create local facts for latter test
+ copy:
+ src: uuid.fact
+ dest: /etc/ansible/facts.d/
+ mode: 0755
+
+- hosts: testhost
+ tasks:
+ - name: ensure we still have facts, but didnt pickup new local ones
+ assert:
+ that:
+ - ansible_facts is defined and 'fqdn' in ansible_facts
+ - not 'uuid' in ansible_local
+
+ - name: remove local facts file
+ file: path=/etc/ansible/facts.d/uuid.fact state=absent
diff --git a/test/integration/targets/gathering/uuid.fact b/test/integration/targets/gathering/uuid.fact
new file mode 100644
index 0000000000..79e3f62677
--- /dev/null
+++ b/test/integration/targets/gathering/uuid.fact
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+
+import json
+import uuid
+
+
+# return a random string
+print(json.dumps(str(uuid.uuid4())))
diff --git a/test/integration/targets/pull/pull-integration-test/local.yml b/test/integration/targets/pull/pull-integration-test/local.yml
index f251b43473..a7f3a188d1 100644
--- a/test/integration/targets/pull/pull-integration-test/local.yml
+++ b/test/integration/targets/pull/pull-integration-test/local.yml
@@ -1,5 +1,6 @@
- name: test playbook for ansible-pull
hosts: all
+ gather_facts: False
tasks:
- name: debug output
debug: msg="test task"