From 88772b60035bd4bc06629aa8cc5cf76123d447f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yannig=20Perr=C3=A9?= Date: Sat, 12 Mar 2016 10:22:49 +0100 Subject: [PATCH] Add a way to restrict gathered facts in Ansible: - Using gather_subset options - By ignoring ohai/chef or facter/puppet facts --- docsite/rst/glossary.rst | 3 +- docsite/rst/intro_configuration.rst | 23 +++++++++++- examples/ansible.cfg | 15 ++++++++ lib/ansible/constants.py | 3 ++ lib/ansible/executor/play_iterator.py | 21 ++++++++++- lib/ansible/module_utils/facts.py | 29 +++++++++++++-- lib/ansible/playbook/play.py | 3 ++ test/integration/test_gathering_facts.yml | 45 +++++++++++++++++++++++ 8 files changed, 133 insertions(+), 9 deletions(-) create mode 100644 test/integration/test_gathering_facts.yml diff --git a/docsite/rst/glossary.rst b/docsite/rst/glossary.rst index d05481a621e..7c5bd5f812a 100644 --- a/docsite/rst/glossary.rst +++ b/docsite/rst/glossary.rst @@ -71,8 +71,7 @@ Facts Facts are simply things that are discovered about remote nodes. While they can be used in playbooks and templates just like variables, facts are things that are inferred, rather than set. Facts are automatically discovered by Ansible when running plays by executing the internal 'setup' module on the remote nodes. You never have to call the setup module explicitly, it just runs, but it can be disabled to save time if it is -not needed. For the convenience of users who are switching from other configuration management systems, the fact module will also pull in facts from the 'ohai' and 'facter' -tools if they are installed, which are fact libraries from Chef and Puppet, respectively. +not needed or to reduce to a subset. For the convenience of users who are switching from other configuration management systems, the fact module will also pull in facts from the 'ohai' and 'facter' tools if they are installed, which are fact libraries from Chef and Puppet, respectively. You can also ignore them and save time at runtime execution. Filter Plugin +++++++++++++ diff --git a/docsite/rst/intro_configuration.rst b/docsite/rst/intro_configuration.rst index 0bc6fbfad7b..ddcf6736188 100644 --- a/docsite/rst/intro_configuration.rst +++ b/docsite/rst/intro_configuration.rst @@ -353,6 +353,25 @@ This option can be useful for those wishing to save fact gathering time. Both 's gathering = smart +.. versionadded:: 2.1 + +You can specify a subset of gathered facts using the following options: + + gather_subset = all + +:all: gather all subsets +:min: gather a very limited set of facts +:network: gather min and network facts +:hardware: gather min and hardware facts (longest facts to retrieve) +:virtual: gather min and virtual facts + +You can combine them using comma separated list (ex: min,network,virtual) + +You can also disable puppet facter or chef ohai facts collection using following options: + + ignore_ohai = True + ignore_facter = True + hash_behaviour ============== @@ -367,7 +386,7 @@ official examples repos do not use this setting:: The valid values are either 'replace' (the default) or 'merge'. -.. versionadded: '2.0' +.. versionadded:: 2.0 If you want to merge hashes without changing the global settings, use the `combine` filter described in :doc:`playbooks_filters`. @@ -585,7 +604,7 @@ The directory will be created if it does not already exist. roles_path ========== -.. versionadded: '1.4' +.. versionadded:: 1.4 The roles path indicate additional directories beyond the 'roles/' subdirectory of a playbook project to search to find Ansible roles. For instance, if there was a source control repository of common roles and a different repository of playbooks, you might diff --git a/examples/ansible.cfg b/examples/ansible.cfg index 8465ccca4bb..181630f9c64 100644 --- a/examples/ansible.cfg +++ b/examples/ansible.cfg @@ -31,6 +31,21 @@ # explicit - do not gather by default, must say gather_facts: True #gathering = implicit +# by default retrieve all facts subsets +# all - gather all subsets +# min - gather a very limited set of facts +# network - gather min and network facts +# hardware - gather hardware facts (longest facts to retrieve) +# virtual - gather min and virtual facts +# You can combine them using comma (ex: min,network,virtual) +#gather_subset = all + +# by default run ohai +#ignore_ohai = False + +# by default run facter +#ignore_facter = False + # additional paths to search for roles in, colon separated #roles_path = /etc/ansible/roles diff --git a/lib/ansible/constants.py b/lib/ansible/constants.py index 4def61e1aa0..7e91233fcae 100644 --- a/lib/ansible/constants.py +++ b/lib/ansible/constants.py @@ -156,6 +156,9 @@ DEFAULT_PRIVATE_ROLE_VARS = get_config(p, DEFAULTS, 'private_role_vars', 'ANSIBL DEFAULT_JINJA2_EXTENSIONS = get_config(p, DEFAULTS, 'jinja2_extensions', 'ANSIBLE_JINJA2_EXTENSIONS', None) DEFAULT_EXECUTABLE = get_config(p, DEFAULTS, 'executable', 'ANSIBLE_EXECUTABLE', '/bin/sh') DEFAULT_GATHERING = get_config(p, DEFAULTS, 'gathering', 'ANSIBLE_GATHERING', 'implicit').lower() +DEFAULT_GATHER_SUBSET = get_config(p, DEFAULTS, 'gather_subset', 'ANSIBLE_GATHER_SUBSET', 'all').lower() +DEFAULT_IGNORE_OHAI = get_config(p, DEFAULTS, 'ignore_ohai', 'ANSIBLE_IGNORE_OHAI', False, boolean=True) +DEFAULT_IGNORE_FACTER = get_config(p, DEFAULTS, 'ignore_facter', 'ANSIBLE_IGNORE_FACTER', False, boolean=True) DEFAULT_LOG_PATH = get_config(p, DEFAULTS, 'log_path', 'ANSIBLE_LOG_PATH', '', ispath=True) DEFAULT_FORCE_HANDLERS = get_config(p, DEFAULTS, 'force_handlers', 'ANSIBLE_FORCE_HANDLERS', False, boolean=True) DEFAULT_INVENTORY_IGNORE = get_config(p, DEFAULTS, 'inventory_ignore_extensions', 'ANSIBLE_INVENTORY_IGNORE', ["~", ".orig", ".bak", ".ini", ".cfg", ".retry", ".pyc", ".pyo"], islist=True) diff --git a/lib/ansible/executor/play_iterator.py b/lib/ansible/executor/play_iterator.py index 83abb40bbc1..93321ce8ae6 100644 --- a/lib/ansible/executor/play_iterator.py +++ b/lib/ansible/executor/play_iterator.py @@ -151,11 +151,30 @@ class PlayIterator: self._play = play self._blocks = [] + # Default options to gather + gather_subset = C.DEFAULT_GATHER_SUBSET + ignore_ohai = C.DEFAULT_IGNORE_OHAI + ignore_facter = C.DEFAULT_IGNORE_FACTER + + # Retrieve subset to gather + if self._play.gather_subset is not None: + gather_subset = self._play.gather_subset + # ignore ohai + if self._play.ignore_ohai is not None: + ignore_ohai = self._play.ignore_ohai + # ignore puppet facter + if self._play.ignore_facter is not None: + ignore_facter = self._play.ignore_facter + setup_block = Block(play=self._play) setup_task = Task(block=setup_block) setup_task.action = 'setup' setup_task.tags = ['always'] - setup_task.args = {} + setup_task.args = { + 'gather_subset': gather_subset, + 'ignore_ohai' : ignore_ohai, + 'ignore_facter': ignore_facter, + } setup_task.set_loader(self._play._loader) setup_block.block = [setup_task] diff --git a/lib/ansible/module_utils/facts.py b/lib/ansible/module_utils/facts.py index 1aa16c9feeb..d683ee2a260 100644 --- a/lib/ansible/module_utils/facts.py +++ b/lib/ansible/module_utils/facts.py @@ -159,6 +159,9 @@ class Facts(object): { 'path' : '/usr/local/sbin/pkg', 'name' : 'pkgng' }, ] + # Allowed fact subset for gather_subset options + ALLOWED_FACT_SUBSET = frozenset([ 'all', 'min', 'network', 'hardware', 'virtual' ]) + def __init__(self, load_on_init=True): self.facts = {} @@ -3067,15 +3070,33 @@ def get_file_lines(path): return ret def ansible_facts(module): + # Retrieve module parameters + gather_subset = [ 'all' ] + if 'gather_subset' in module.params: + gather_subset = module.params['gather_subset'] + + # Retrieve all facts elements + if 'all' in gather_subset: + gather_subset = [ 'min', 'hardware', 'network', 'virtual' ] + + # Check subsets and forbid unallowed name + for subset in gather_subset: + if subset not in Facts.ALLOWED_FACT_SUBSET: + raise TypeError("Bad subset '%s' given to Ansible. gather_subset options allowed: %s" % (subset, ", ".join(Facts.ALLOWED_FACT_SUBSET))) + facts = {} facts.update(Facts().populate()) - facts.update(Hardware().populate()) - facts.update(Network(module).populate()) - facts.update(Virtual().populate()) + if 'hardware' in gather_subset: + facts.update(Hardware().populate()) + if 'network' in gather_subset: + facts.update(Network(module).populate()) + if 'virtual' in gather_subset: + facts.update(Virtual().populate()) + facts['gather_subset'] = gather_subset return facts # =========================================== - +# TODO: remove this dead code? def get_all_facts(module): setup_options = dict(module_setup=True) diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py index c354b745496..c001419732a 100644 --- a/lib/ansible/playbook/play.py +++ b/lib/ansible/playbook/play.py @@ -64,6 +64,9 @@ class Play(Base, Taggable, Become): # Connection _gather_facts = FieldAttribute(isa='bool', default=None, always_post_validate=True) + _gather_subset = FieldAttribute(isa='string', default=None, always_post_validate=True) + _ignore_facter = FieldAttribute(isa='bool', default=None, always_post_validate=True) + _ignore_ohai = FieldAttribute(isa='bool', default=None, always_post_validate=True) _hosts = FieldAttribute(isa='list', required=True, listof=string_types, always_post_validate=True) _name = FieldAttribute(isa='string', default='', always_post_validate=True) diff --git a/test/integration/test_gathering_facts.yml b/test/integration/test_gathering_facts.yml new file mode 100644 index 00000000000..03d707fbf1a --- /dev/null +++ b/test/integration/test_gathering_facts.yml @@ -0,0 +1,45 @@ +--- + +- hosts: localhost + tags: [ 'min' ] + connection: local + gather_subset: "min" + ignore_facter: yes + ignore_ohai: yes + gather_facts: yes + tasks: + - debug: var={{item}} + with_items: [ 'ansible_user_id', 'ansible_interfaces', 'ansible_mounts', 'ansible_virtualization_role' ] + +- hosts: localhost + tags: [ 'network' ] + connection: local + gather_subset: "network" + ignore_facter: yes + ignore_ohai: yes + gather_facts: yes + tasks: + - debug: var={{item}} + with_items: [ 'ansible_user_id', 'ansible_interfaces', 'ansible_mounts', 'ansible_virtualization_role' ] + +- hosts: localhost + tags: [ 'hardware' ] + connection: local + gather_subset: "hardware" + ignore_facter: yes + ignore_ohai: yes + gather_facts: yes + tasks: + - debug: var={{item}} + with_items: [ 'ansible_user_id', 'ansible_interfaces', 'ansible_mounts', 'ansible_virtualization_role' ] + +- hosts: localhost + tags: [ 'virtual' ] + connection: local + gather_subset: "virtual" + ignore_facter: yes + ignore_ohai: yes + gather_facts: yes + tasks: + - debug: var={{item}} + with_items: [ 'ansible_user_id', 'ansible_interfaces', 'ansible_mounts', 'ansible_virtualization_role' ]