Like most plugin types (except modules) they must be developed in Python, since they execute on the controller they should match the same requirements :ref:`control_machine_requirements`.
Most of the documentation in :ref:`developing_plugins` also applies here, so as to not repeat ourselves, you should read that document first and we'll include inventory plugin specifics next.
Inventory plugins normally only execute at the start of a run, before playbooks/plays and roles are loaded,
but they can be 're-executed' via the ``meta: refresh_inventory`` task, which will clear out the existing inventory and rebuild it.
When using the 'persistent' cache, inventory plugins can also use the configured cache plugin to store and retrieve data to avoid costly external calls.
.._developing_an_inventory_plugin:
Developing an Inventory Plugin
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The first thing you want to do is use the base class:
..code-block:: python
from ansible.plugins.inventory import BaseInventoryPlugin
class InventoryModule(BaseInventoryPlugin):
NAME = 'myplugin' # used internally by Ansible, it should match the file name but not required
This class has a couple of methods each plugin should implement and a few helpers for parsing the inventory source and updating the inventory.
After you have the basic plugin working you might want to to incorporate other features by adding more base classes:
..code-block:: python
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable
class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
NAME = 'myplugin'
For the bulk of the work in the plugin, We mostly want to deal with 2 methods ``verify_file`` and ``parse``.
.._inventory_plugin_verify_file:
verify_file
"""""""""""
This method is used by Ansible to make a quick determination if the inventory source is usable by the plugin. It does not need to be 100% accurate as there might be overlap in what plugins can handle and Ansible will try the enabled plugins (in order) by default.
..code-block:: python
def verify_file(self, path):
''' return true/false if this is possibly a valid file for this plugin to consume '''
valid = False
if super(InventoryModule, self).verify_file(path):
# base class verifies that file exists and is readable by current user
if path.endswith(('.vbox.yaml', '.vbox.yml')):
valid = True
return valid
In this case, from the :ref:`virtualbox inventory plugin <virtualbox_inventory>`, we screen for specific file name patterns to avoid attempting to consume any valid yaml file. You can add any type of condition here, but the most common one is 'extension matching'
Another example that actually does not use a 'file' but the inventory source string itself,
from the :ref:`host list <host_list_inventory>` plugin:
..code-block:: python
def verify_file(self, path):
''' don't call base class as we don't expect a path, but a host list '''
if not os.path.exists(b_path) and ',' in host_list:
# the path does NOT exist and there is a comma to indicate this is a 'host list'
valid = True
return valid
This method is just to expedite the inventory process and avoid uneccessary parsing of sources that are easy to filter out before causing a parse error.
.._inventory_plugin_parse:
parse
"""""
This method does the bulk of the work in the plugin.
It takes the following paramters:
* inventory: inventory object with existing data and the methods to add hosts/groups/variables to inventory
* loader: Ansible's DataLoader. The DataLoader can read files, auto load JSON/YAML and decrypt vaulted data, and cache read files.
* path: string with inventory source (this is usually a path, but is not required)
* cache: indicates whether the plugin should use or avoid caches (cache plugin and/or loader)
The base class does some minimal assignment for reuse in other methods.
The specifics will vary depending on API and structure returned. But one thing to keep in mind, if the inventory source or any other issue crops up you should ``raise AnsibleParserError`` to let Ansible know that the source was invalid or the process failed.
For examples on how to implement an inventory plug in, see the source code here:
To simplify development, most plugins use a mostly standard configuration file as the inventory source, YAML based and with just one required field ``plugin`` which should contain the name of the plugin that is expected to consume the file.
Depending on other common features used, other fields might be needed, but each plugin can also add it's own custom options as needed.
For example, if you use the integrated caching, ``cache_plugin``, ``cache_timeout`` and other cache related fields could be present.
.._inventory_development_auto:
The 'auto' plugin
^^^^^^^^^^^^^^^^^
Since Ansible 2.5, we include the :ref:`auto inventory plugin <auto_inventory>` enabled by default, which itself just loads other plugins if they use the common YAML configuration format that specifies a ``plugin`` field that matches an inventory plugin name, this makes it easier to use your plugin w/o having to update configurations.
.._inventory_scripts:
.._developing_inventory_scripts:
Inventory Scripts
-----------------
Even though we now have inventory plugins, we still support inventory scripts, not only for backwards compatibility but also to allow users to leverage other programming languages.
When called with the argument ``--host <hostname>`` (where <hostname> is a host from above), the script must print either an empty JSON hash/dictionary, or a hash/dictionary of variables to make available to templates and playbooks. For example::
To satisfy the requirements of using ``_meta``, to prevent ansible from calling your inventory with ``--host`` you must at least populate ``_meta`` with an empty ``hostvars`` dictionary.
An easy way to see how this should look is using :ref:`ansible-inventory`, which also supports ``--list`` and ``--host`` parameters like an inventory script would.