diff options
Diffstat (limited to 'lib/ansible/vars/plugins.py')
-rw-r--r-- | lib/ansible/vars/plugins.py | 112 |
1 files changed, 51 insertions, 61 deletions
diff --git a/lib/ansible/vars/plugins.py b/lib/ansible/vars/plugins.py index c2343507..303052b3 100644 --- a/lib/ansible/vars/plugins.py +++ b/lib/ansible/vars/plugins.py @@ -1,48 +1,33 @@ # Copyright (c) 2018 Ansible Project # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -from __future__ import annotations +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type import os -from functools import lru_cache - from ansible import constants as C from ansible.errors import AnsibleError -from ansible.inventory.group import InventoryObjectType +from ansible.inventory.host import Host +from ansible.module_utils._text import to_bytes from ansible.plugins.loader import vars_loader +from ansible.utils.collection_loader import AnsibleCollectionRef from ansible.utils.display import Display from ansible.utils.vars import combine_vars display = Display() -def _prime_vars_loader(): - # find 3rd party legacy vars plugins once, and look them up by name subsequently - list(vars_loader.all(class_only=True)) - for plugin_name in C.VARIABLE_PLUGINS_ENABLED: - if not plugin_name: - continue - vars_loader.get(plugin_name) - - def get_plugin_vars(loader, plugin, path, entities): data = {} try: data = plugin.get_vars(loader, path, entities) except AttributeError: - if hasattr(plugin, 'get_host_vars') or hasattr(plugin, 'get_group_vars'): - display.deprecated( - f"The vars plugin {plugin.ansible_name} from {plugin._original_path} is relying " - "on the deprecated entrypoints 'get_host_vars' and 'get_group_vars'. " - "This plugin should be updated to inherit from BaseVarsPlugin and define " - "a 'get_vars' method as the main entrypoint instead.", - version="2.20", - ) try: for entity in entities: - if entity.base_type is InventoryObjectType.HOST: + if isinstance(entity, Host): data |= plugin.get_host_vars(entity.name) else: data |= plugin.get_group_vars(entity.name) @@ -54,53 +39,59 @@ def get_plugin_vars(loader, plugin, path, entities): return data -# optimized for stateless plugins; non-stateless plugin instances will fall out quickly -@lru_cache(maxsize=10) -def _plugin_should_run(plugin, stage): - # if a plugin-specific setting has not been provided, use the global setting - # older/non shipped plugins that don't support the plugin-specific setting should also use the global setting - allowed_stages = None - - try: - allowed_stages = plugin.get_option('stage') - except (AttributeError, KeyError): - pass - - if allowed_stages: - return allowed_stages in ('all', stage) - - # plugin didn't declare a preference; consult global config - config_stage_override = C.RUN_VARS_PLUGINS - if config_stage_override == 'demand' and stage == 'inventory': - return False - elif config_stage_override == 'start' and stage == 'task': - return False - return True - - def get_vars_from_path(loader, path, entities, stage): - data = {} - if vars_loader._paths is None: - # cache has been reset, reload all() - _prime_vars_loader() - for plugin_name in vars_loader._plugin_instance_cache: - if (plugin := vars_loader.get(plugin_name)) is None: - continue + data = {} - collection = '.' in plugin.ansible_name and not plugin.ansible_name.startswith('ansible.builtin.') + vars_plugin_list = list(vars_loader.all()) + for plugin_name in C.VARIABLE_PLUGINS_ENABLED: + if AnsibleCollectionRef.is_valid_fqcr(plugin_name): + vars_plugin = vars_loader.get(plugin_name) + if vars_plugin is None: + # Error if there's no play directory or the name is wrong? + continue + if vars_plugin not in vars_plugin_list: + vars_plugin_list.append(vars_plugin) + + for plugin in vars_plugin_list: + # legacy plugins always run by default, but they can set REQUIRES_ENABLED=True to opt out. + + builtin_or_legacy = plugin.ansible_name.startswith('ansible.builtin.') or '.' not in plugin.ansible_name + + # builtin is supposed to have REQUIRES_ENABLED=True, the following is for legacy plugins... + needs_enabled = not builtin_or_legacy + if hasattr(plugin, 'REQUIRES_ENABLED'): + needs_enabled = plugin.REQUIRES_ENABLED + elif hasattr(plugin, 'REQUIRES_WHITELIST'): + display.deprecated("The VarsModule class variable 'REQUIRES_WHITELIST' is deprecated. " + "Use 'REQUIRES_ENABLED' instead.", version=2.18) + needs_enabled = plugin.REQUIRES_WHITELIST + + # A collection plugin was enabled to get to this point because vars_loader.all() does not include collection plugins. # Warn if a collection plugin has REQUIRES_ENABLED because it has no effect. - if collection and (hasattr(plugin, 'REQUIRES_ENABLED') or hasattr(plugin, 'REQUIRES_WHITELIST')): + if not builtin_or_legacy and (hasattr(plugin, 'REQUIRES_ENABLED') or hasattr(plugin, 'REQUIRES_WHITELIST')): display.warning( "Vars plugins in collections must be enabled to be loaded, REQUIRES_ENABLED is not supported. " "This should be removed from the plugin %s." % plugin.ansible_name ) + elif builtin_or_legacy and needs_enabled and not plugin.matches_name(C.VARIABLE_PLUGINS_ENABLED): + continue + + has_stage = hasattr(plugin, 'get_option') and plugin.has_option('stage') + + # if a plugin-specific setting has not been provided, use the global setting + # older/non shipped plugins that don't support the plugin-specific setting should also use the global setting + use_global = (has_stage and plugin.get_option('stage') is None) or not has_stage - if not _plugin_should_run(plugin, stage): + if use_global: + if C.RUN_VARS_PLUGINS == 'demand' and stage == 'inventory': + continue + elif C.RUN_VARS_PLUGINS == 'start' and stage == 'task': + continue + elif has_stage and plugin.get_option('stage') not in ('all', stage): continue - if (new_vars := get_plugin_vars(loader, plugin, path, entities)) != {}: - data = combine_vars(data, new_vars) + data = combine_vars(data, get_plugin_vars(loader, plugin, path, entities)) return data @@ -114,11 +105,10 @@ def get_vars_from_inventory_sources(loader, sources, entities, stage): continue if ',' in path and not os.path.exists(path): # skip host lists continue - elif not os.path.isdir(path): + elif not os.path.isdir(to_bytes(path)): # always pass the directory of the inventory source file path = os.path.dirname(path) - if (new_vars := get_vars_from_path(loader, path, entities, stage)) != {}: - data = combine_vars(data, new_vars) + data = combine_vars(data, get_vars_from_path(loader, path, entities, stage)) return data |