From 5ff36c34234198a5f143b726727bb8db664afb0e Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Thu, 9 Nov 2017 16:38:34 -0800 Subject: [PATCH] Enable autoloading of inventory plugins (#32709) * Automatically loads and executes an inventory plugin specified by a standard YAML inventory config file containing a `plugin` key at its root. * Moved inventory PluginLoader to a shared global instance. --- lib/ansible/cli/doc.py | 4 +- lib/ansible/config/base.yml | 2 +- lib/ansible/inventory/manager.py | 3 +- lib/ansible/plugins/inventory/auto.py | 56 +++++++++++++++++++++++++++ lib/ansible/plugins/loader.py | 7 ++++ 5 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 lib/ansible/plugins/inventory/auto.py diff --git a/lib/ansible/cli/doc.py b/lib/ansible/cli/doc.py index 570c02e5b82..7e84f6bd515 100644 --- a/lib/ansible/cli/doc.py +++ b/lib/ansible/cli/doc.py @@ -29,7 +29,7 @@ from ansible.module_utils._text import to_native from ansible.module_utils.six import string_types from ansible.parsing.yaml.dumper import AnsibleDumper from ansible.plugins.loader import module_loader, action_loader, lookup_loader, callback_loader, cache_loader, \ - vars_loader, connection_loader, strategy_loader, PluginLoader + vars_loader, connection_loader, strategy_loader, inventory_loader from ansible.utils import plugin_docs try: from __main__ import display @@ -97,7 +97,7 @@ class DocCLI(CLI): elif plugin_type == 'vars': loader = vars_loader elif plugin_type == 'inventory': - loader = PluginLoader('InventoryModule', 'ansible.plugins.inventory', C.DEFAULT_INVENTORY_PLUGIN_PATH, 'inventory_plugins') + loader = inventory_loader else: loader = module_loader diff --git a/lib/ansible/config/base.yml b/lib/ansible/config/base.yml index 327f506a625..e417c816b95 100644 --- a/lib/ansible/config/base.yml +++ b/lib/ansible/config/base.yml @@ -1239,7 +1239,7 @@ HOST_KEY_CHECKING: type: boolean INVENTORY_ENABLED: name: Active Inventory plugins - default: ['host_list', 'script', 'yaml', 'ini'] + default: ['host_list', 'script', 'yaml', 'ini', 'auto'] description: List of enabled inventory plugins, it also determines the order in which they are used. env: [{name: ANSIBLE_INVENTORY_ENABLED}] ini: diff --git a/lib/ansible/inventory/manager.py b/lib/ansible/inventory/manager.py index 09f96f64555..13ea13d4efd 100644 --- a/lib/ansible/inventory/manager.py +++ b/lib/ansible/inventory/manager.py @@ -30,7 +30,7 @@ from ansible.inventory.data import InventoryData from ansible.module_utils.six import string_types from ansible.module_utils._text import to_bytes, to_native, to_text from ansible.parsing.utils.addresses import parse_address -from ansible.plugins.loader import PluginLoader +from ansible.plugins.loader import inventory_loader from ansible.utils.path import unfrackpath try: @@ -178,7 +178,6 @@ class InventoryManager(object): def _setup_inventory_plugins(self): ''' sets up loaded inventory plugins for usage ''' - inventory_loader = PluginLoader('InventoryModule', 'ansible.plugins.inventory', C.DEFAULT_INVENTORY_PLUGIN_PATH, 'inventory_plugins') display.vvvv('setting up inventory plugins') for name in C.INVENTORY_ENABLED: diff --git a/lib/ansible/plugins/inventory/auto.py b/lib/ansible/plugins/inventory/auto.py new file mode 100644 index 00000000000..c2bd7f8ef57 --- /dev/null +++ b/lib/ansible/plugins/inventory/auto.py @@ -0,0 +1,56 @@ +# 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 + +DOCUMENTATION = ''' + name: auto + plugin_type: inventory + authors: + - Matt Davis <@nitzmahone> + short_description: Loads and executes an inventory plugin specified in a YAML config + description: + - By whitelisting C(auto) as the final inventory plugin, any YAML inventory config file with a + C(plugin) key at its root will automatically cause the named plugin to be loaded and executed with that + config. This effectively provides automatic whitelisting of all installed/accessible inventory plugins. + - To disable this behavior, remove C(auto) from the C(INVENTORY_ENABLED) config element. + options: +''' + +EXAMPLES = ''' +# This plugin is not intended for direct use; it is a fallback mechanism for automatic whitelisting of +# all installed inventory plugins. +''' + +from ansible.errors import AnsibleParserError +from ansible.plugins.inventory import BaseInventoryPlugin +from ansible.plugins.loader import inventory_loader + + +class InventoryModule(BaseInventoryPlugin): + + NAME = 'auto' + + def verify_file(self, path): + if not path.endswith('.yml') and not path.endswith('.yaml'): + return False + return super(InventoryModule, self).verify_file(path) + + def parse(self, inventory, loader, path, cache=True): + config_data = loader.load_from_file(path) + + plugin_name = config_data.get('plugin') + + if not plugin_name: + raise AnsibleParserError("no root 'plugin' key found, '{0}' is not a valid YAML inventory plugin config file".format(path)) + + plugin = inventory_loader.get(plugin_name) + + if not plugin: + raise AnsibleParserError("inventory config '{0}' specifies unknown plugin '{1}'".format(path, plugin_name)) + + if not plugin.verify_file(path): + raise AnsibleParserError("inventory config '{0}' could not be verified by plugin '{1}'".format(path, plugin_name)) + + plugin.parse(inventory, loader, path, cache=cache) diff --git a/lib/ansible/plugins/loader.py b/lib/ansible/plugins/loader.py index 1e1dc84a6c0..aa955724202 100644 --- a/lib/ansible/plugins/loader.py +++ b/lib/ansible/plugins/loader.py @@ -582,3 +582,10 @@ netconf_loader = PluginLoader( 'netconf_plugins', required_base_class='NetconfBase' ) + +inventory_loader = PluginLoader( + 'InventoryModule', + 'ansible.plugins.inventory', + C.DEFAULT_INVENTORY_PLUGIN_PATH, + 'inventory_plugins' +)