Configurable list of facts modules (#31783)

* configurable list of facts modules

 - allow for args dict for specific modules
 - add way to pass parameters
 - avoid facts poluting test
 - move to 'facts gathered' flag
 - add 'gathering' setting tests
pull/40021/head
Brian Coca 7 years ago committed by GitHub
parent 7c510a2549
commit 95655fae5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1284,6 +1284,17 @@ ERROR_ON_MISSING_HANDLER:
ini: ini:
- {key: error_on_missing_handler, section: defaults} - {key: error_on_missing_handler, section: defaults}
type: boolean 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: GALAXY_IGNORE_CERTS:
name: Galaxy validate certs name: Galaxy validate certs
default: False default: False

@ -173,7 +173,7 @@ class PlayIterator:
# the others. # the others.
setup_block.run_once = False setup_block.run_once = False
setup_task = Task(block=setup_block) setup_task = Task(block=setup_block)
setup_task.action = 'setup' setup_task.action = 'gather_facts'
setup_task.name = 'Gathering Facts' setup_task.name = 'Gathering Facts'
setup_task.tags = ['always'] setup_task.tags = ['always']
setup_task.args = { setup_task.args = {
@ -301,7 +301,7 @@ class PlayIterator:
if (gathering == 'implicit' and implied) or \ if (gathering == 'implicit' and implied) or \
(gathering == 'explicit' and boolean(self._play.gather_facts, strict=False)) 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 # The setup block is always self._blocks[0], as we inject it
# during the play compilation in __init__ above. # during the play compilation in __init__ above.
setup_block = self._blocks[0] setup_block = self._blocks[0]

@ -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
"""

@ -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

@ -328,7 +328,7 @@ class PluginLoader:
from ansible.vars.reserved import is_reserved_name 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) 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( raise AnsibleError(
'Module "%s" shadows the name of a reserved keyword. Please rename or remove this module. Found at %s' % (name, plugin) 'Module "%s" shadows the name of a reserved keyword. Please rename or remove this module. Found at %s' % (name, plugin)
) )

@ -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

@ -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

@ -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 "$@"

@ -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

@ -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())))

@ -1,5 +1,6 @@
- name: test playbook for ansible-pull - name: test playbook for ansible-pull
hosts: all hosts: all
gather_facts: False
tasks: tasks:
- name: debug output - name: debug output
debug: msg="test task" debug: msg="test task"

Loading…
Cancel
Save