diff --git a/examples/ansible.cfg b/examples/ansible.cfg index f897a56bfb..54a1008295 100644 --- a/examples/ansible.cfg +++ b/examples/ansible.cfg @@ -45,6 +45,13 @@ # A minimal set of facts is always gathered. #gather_subset = all +# some hardware related facts are collected +# with a maximum timeout of 10 seconds. This +# option lets you increase or decrease that +# timeout to something more suitable for the +# environment. +# gather_timeout = 10 + # 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 6e70d3c0d8..9e20ac5bf7 100644 --- a/lib/ansible/constants.py +++ b/lib/ansible/constants.py @@ -177,6 +177,7 @@ DEFAULT_JINJA2_EXTENSIONS = get_config(p, DEFAULTS, 'jinja2_extensions', 'ANSIBL 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_GATHER_TIMEOUT = get_config(p, DEFAULTS, 'gather_timeout', 'ANSIBLE_GATHER_TIMEOUT', 10, integer=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 4613d75fb6..51d066ead3 100644 --- a/lib/ansible/executor/play_iterator.py +++ b/lib/ansible/executor/play_iterator.py @@ -156,10 +156,14 @@ class PlayIterator: # Default options to gather gather_subset = C.DEFAULT_GATHER_SUBSET + gather_timeout = C.DEFAULT_GATHER_TIMEOUT # Retrieve subset to gather if self._play.gather_subset is not None: gather_subset = self._play.gather_subset + # Retrieve timeout for gather + if self._play.gather_timeout is not None: + gather_timeout = self._play.gather_timeout setup_block = Block(play=self._play) setup_task = Task(block=setup_block) @@ -168,6 +172,8 @@ class PlayIterator: setup_task.args = { 'gather_subset': gather_subset, } + if gather_timeout: + setup_task.args['gather_timeout'] = gather_timeout 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 2f4d30c875..f5a8fe9c56 100644 --- a/lib/ansible/module_utils/facts.py +++ b/lib/ansible/module_utils/facts.py @@ -113,15 +113,21 @@ if platform.system() != 'SunOS': # timeout function to make sure some fact gathering # steps do not exceed a time limit +GATHER_TIMEOUT=None + class TimeoutError(Exception): pass def timeout(seconds=10, error_message="Timer expired"): + def decorator(func): def _handle_timeout(signum, frame): raise TimeoutError(error_message) def wrapper(*args, **kwargs): + if 'GATHER_TIMEOUT' in globals(): + if GATHER_TIMEOUT: + seconds = GATHER_TIMEOUT signal.signal(signal.SIGALRM, _handle_timeout) signal.alarm(seconds) try: @@ -3225,6 +3231,9 @@ def get_all_facts(module): # Retrieve module parameters gather_subset = module.params['gather_subset'] + global GATHER_TIMEOUT + GATHER_TIMEOUT = module.params['gather_timeout'] + # Retrieve all facts elements additional_subsets = set() exclude_subsets = set() diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py index 2031465eef..90951e1e18 100644 --- a/lib/ansible/playbook/play.py +++ b/lib/ansible/playbook/play.py @@ -65,6 +65,7 @@ class Play(Base, Taggable, Become): # Connection _gather_facts = FieldAttribute(isa='bool', default=None, always_post_validate=True) _gather_subset = FieldAttribute(isa='barelist', default=None, always_post_validate=True) + _gather_timeout = FieldAttribute(isa='int', 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)