Improve argument spec documentation (#72335)
* Link to argument spec documentation from 'Creating a module'. * Use items instead of sub-headings. * Update deprecation information. * Document conditional requirements. * Document supports_check_mode and add_file_common_args. * Add deprecation examples. * Re-add word. * Improve title. * Fix RST formatting, add conditional dependency section starter.
This commit is contained in:
parent
569d937df8
commit
01d207a3e3
2 changed files with 291 additions and 54 deletions
|
@ -75,7 +75,7 @@ To create an info module:
|
||||||
|
|
||||||
1. Navigate to the correct directory for your new module: ``$ cd lib/ansible/modules/``. If you are developing module using collection, ``$ cd plugins/modules/`` inside your collection development tree.
|
1. Navigate to the correct directory for your new module: ``$ cd lib/ansible/modules/``. If you are developing module using collection, ``$ cd plugins/modules/`` inside your collection development tree.
|
||||||
2. Create your new module file: ``$ touch my_test_info.py``.
|
2. Create your new module file: ``$ touch my_test_info.py``.
|
||||||
3. Paste the content below into your new info module file. It includes the :ref:`required Ansible format and documentation <developing_modules_documenting>` and some example code.
|
3. Paste the content below into your new info module file. It includes the :ref:`required Ansible format and documentation <developing_modules_documenting>`, a simple :ref:`argument spec for declaring the module options <argument_spec>`, and some example code.
|
||||||
4. Modify and extend the code to do what you want your new info module to do. See the :ref:`programming tips <developing_modules_best_practices>` and :ref:`Python 3 compatibility <developing_python_3>` pages for pointers on writing clean and concise module code.
|
4. Modify and extend the code to do what you want your new info module to do. See the :ref:`programming tips <developing_modules_best_practices>` and :ref:`Python 3 compatibility <developing_python_3>` pages for pointers on writing clean and concise module code.
|
||||||
|
|
||||||
.. literalinclude:: ../../../../examples/scripts/my_test_info.py
|
.. literalinclude:: ../../../../examples/scripts/my_test_info.py
|
||||||
|
@ -93,7 +93,7 @@ To create a module:
|
||||||
|
|
||||||
1. Navigate to the correct directory for your new module: ``$ cd lib/ansible/modules/``. If you are developing a module in a :ref:`collection <developing_collections>`, ``$ cd plugins/modules/`` inside your collection development tree.
|
1. Navigate to the correct directory for your new module: ``$ cd lib/ansible/modules/``. If you are developing a module in a :ref:`collection <developing_collections>`, ``$ cd plugins/modules/`` inside your collection development tree.
|
||||||
2. Create your new module file: ``$ touch my_test.py``.
|
2. Create your new module file: ``$ touch my_test.py``.
|
||||||
3. Paste the content below into your new module file. It includes the :ref:`required Ansible format and documentation <developing_modules_documenting>` and some example code.
|
3. Paste the content below into your new module file. It includes the :ref:`required Ansible format and documentation <developing_modules_documenting>`, a simple :ref:`argument spec for declaring the module options <argument_spec>`, and some example code.
|
||||||
4. Modify and extend the code to do what you want your new module to do. See the :ref:`programming tips <developing_modules_best_practices>` and :ref:`Python 3 compatibility <developing_python_3>` pages for pointers on writing clean and concise module code.
|
4. Modify and extend the code to do what you want your new module to do. See the :ref:`programming tips <developing_modules_best_practices>` and :ref:`Python 3 compatibility <developing_python_3>` pages for pointers on writing clean and concise module code.
|
||||||
|
|
||||||
.. literalinclude:: ../../../../examples/scripts/my_test.py
|
.. literalinclude:: ../../../../examples/scripts/my_test.py
|
||||||
|
|
|
@ -561,83 +561,320 @@ Example ``argument_spec``:
|
||||||
|
|
||||||
This section will discuss the behavioral attributes for arguments:
|
This section will discuss the behavioral attributes for arguments:
|
||||||
|
|
||||||
type
|
:type:
|
||||||
""""
|
|
||||||
|
|
||||||
``type`` allows you to define the type of the value accepted for the argument. The default value for ``type`` is ``str``. Possible values are:
|
``type`` allows you to define the type of the value accepted for the argument. The default value for ``type`` is ``str``. Possible values are:
|
||||||
|
|
||||||
* str
|
* str
|
||||||
* list
|
* list
|
||||||
* dict
|
* dict
|
||||||
* bool
|
* bool
|
||||||
* int
|
* int
|
||||||
* float
|
* float
|
||||||
* path
|
* path
|
||||||
* raw
|
* raw
|
||||||
* jsonarg
|
* jsonarg
|
||||||
* json
|
* json
|
||||||
* bytes
|
* bytes
|
||||||
* bits
|
* bits
|
||||||
|
|
||||||
The ``raw`` type, performs no type validation or type casting, and maintains the type of the passed value.
|
The ``raw`` type, performs no type validation or type casting, and maintains the type of the passed value.
|
||||||
|
|
||||||
elements
|
:elements:
|
||||||
""""""""
|
|
||||||
|
|
||||||
``elements`` works in combination with ``type`` when ``type='list'``. ``elements`` can then be defined as ``elements='int'`` or any other type, indicating that each element of the specified list should be of that type.
|
``elements`` works in combination with ``type`` when ``type='list'``. ``elements`` can then be defined as ``elements='int'`` or any other type, indicating that each element of the specified list should be of that type.
|
||||||
|
|
||||||
default
|
:default:
|
||||||
"""""""
|
|
||||||
|
|
||||||
The ``default`` option allows sets a default value for the argument for the scenario when the argument is not provided to the module. When not specified, the default value is ``None``.
|
The ``default`` option allows sets a default value for the argument for the scenario when the argument is not provided to the module. When not specified, the default value is ``None``.
|
||||||
|
|
||||||
fallback
|
:fallback:
|
||||||
""""""""
|
|
||||||
|
|
||||||
``fallback`` accepts a ``tuple`` where the first argument is a callable (function) that will be used to perform the lookup, based on the second argument. The second argument is a list of values to be accepted by the callable.
|
``fallback`` accepts a ``tuple`` where the first argument is a callable (function) that will be used to perform the lookup, based on the second argument. The second argument is a list of values to be accepted by the callable.
|
||||||
|
|
||||||
The most common callable used is ``env_fallback`` which will allow an argument to optionally use an environment variable when the argument is not supplied.
|
The most common callable used is ``env_fallback`` which will allow an argument to optionally use an environment variable when the argument is not supplied.
|
||||||
|
|
||||||
Example::
|
Example:
|
||||||
|
|
||||||
username=dict(fallback=(env_fallback, ['ANSIBLE_NET_USERNAME']))
|
.. code-block:: python
|
||||||
|
|
||||||
choices
|
username=dict(fallback=(env_fallback, ['ANSIBLE_NET_USERNAME']))
|
||||||
"""""""
|
|
||||||
|
|
||||||
``choices`` accepts a list of choices that the argument will accept. The types of ``choices`` should match the ``type``.
|
:choices:
|
||||||
|
|
||||||
required
|
``choices`` accepts a list of choices that the argument will accept. The types of ``choices`` should match the ``type``.
|
||||||
""""""""
|
|
||||||
|
|
||||||
``required`` accepts a boolean, either ``True`` or ``False`` that indicates that the argument is required. When not specified, ``required`` defaults to ``False``. This should not be used in combination with ``default``.
|
:required:
|
||||||
|
|
||||||
no_log
|
``required`` accepts a boolean, either ``True`` or ``False`` that indicates that the argument is required. When not specified, ``required`` defaults to ``False``. This should not be used in combination with ``default``.
|
||||||
""""""
|
|
||||||
|
|
||||||
``no_log`` accepts a boolean, either ``True`` or ``False``, that indicates explicitly whether or not the argument value should be masked in logs and output.
|
:no_log:
|
||||||
|
|
||||||
.. note::
|
``no_log`` accepts a boolean, either ``True`` or ``False``, that indicates explicitly whether or not the argument value should be masked in logs and output.
|
||||||
In the absence of ``no_log``, if the parameter name appears to indicate that the argument value is a password or passphrase (such as "admin_password"), a warning will be shown and the value will be masked in logs but **not** output. To disable the warning and masking for parameters that do not contain sensitive information, set ``no_log`` to ``False``.
|
|
||||||
|
|
||||||
aliases
|
.. note::
|
||||||
"""""""
|
In the absence of ``no_log``, if the parameter name appears to indicate that the argument value is a password or passphrase (such as "admin_password"), a warning will be shown and the value will be masked in logs but **not** output. To disable the warning and masking for parameters that do not contain sensitive information, set ``no_log`` to ``False``.
|
||||||
|
|
||||||
``aliases`` accepts a list of alternative argument names for the argument, such as the case where the argument is ``name`` but the module accepts ``aliases=['pkg']`` to allow ``pkg`` to be interchangeably with ``name``
|
:aliases:
|
||||||
|
|
||||||
options
|
``aliases`` accepts a list of alternative argument names for the argument, such as the case where the argument is ``name`` but the module accepts ``aliases=['pkg']`` to allow ``pkg`` to be interchangeably with ``name``
|
||||||
"""""""
|
|
||||||
|
|
||||||
``options`` implements the ability to create a sub-argument_spec, where the sub options of the top level argument are also validated using the attributes discussed in this section. The example at the top of this section demonstrates use of ``options``. ``type`` or ``elements`` should be ``dict`` is this case.
|
:options:
|
||||||
|
|
||||||
apply_defaults
|
``options`` implements the ability to create a sub-argument_spec, where the sub options of the top level argument are also validated using the attributes discussed in this section. The example at the top of this section demonstrates use of ``options``. ``type`` or ``elements`` should be ``dict`` is this case.
|
||||||
""""""""""""""
|
|
||||||
|
|
||||||
``apply_defaults`` works alongside ``options`` and allows the ``default`` of the sub-options to be applied even when the top-level argument is not supplied.
|
:apply_defaults:
|
||||||
|
|
||||||
In the example of the ``argument_spec`` at the top of this section, it would allow ``module.params['top_level']['second_level']`` to be defined, even if the user does not provide ``top_level`` when calling the module.
|
``apply_defaults`` works alongside ``options`` and allows the ``default`` of the sub-options to be applied even when the top-level argument is not supplied.
|
||||||
|
|
||||||
removed_in_version
|
In the example of the ``argument_spec`` at the top of this section, it would allow ``module.params['top_level']['second_level']`` to be defined, even if the user does not provide ``top_level`` when calling the module.
|
||||||
""""""""""""""""""
|
|
||||||
|
|
||||||
``removed_in_version`` indicates which version of Ansible a deprecated argument will be removed in.
|
:removed_in_version:
|
||||||
|
|
||||||
|
``removed_in_version`` indicates which version of ansible-base or a collection a deprecated argument will be removed in. Mutually exclusive with ``removed_at_date``, and must be used with ``removed_from_collection``.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
'option': {
|
||||||
|
'type': 'str',
|
||||||
|
'removed_in_version': '2.0.0',
|
||||||
|
'collection_name': 'testns.testcol',
|
||||||
|
},
|
||||||
|
|
||||||
|
:removed_at_date:
|
||||||
|
|
||||||
|
``removed_at_date`` indicates that a deprecated argument will be removed in a minor ansible-base release or major collection release after this date. Mutually exclusive with ``removed_in_version``, and must be used with ``removed_from_collection``.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
'option': {
|
||||||
|
'type': 'str',
|
||||||
|
'removed_at_date': '2020-12-31',
|
||||||
|
'collection_name': 'testns.testcol',
|
||||||
|
},
|
||||||
|
|
||||||
|
:removed_from_collection:
|
||||||
|
|
||||||
|
Specifies which collection (or ansible-base) deprecates this deprecated argument. Specify ``ansible.builtin`` for ansible-base, or the collection's name (format ``foo.bar``). Must be used with ``removed_in_version`` or ``removed_at_date``.
|
||||||
|
|
||||||
|
:deprecated_aliases:
|
||||||
|
|
||||||
|
Deprecates aliases of this argument. Must contain a list or tuple of dictionaries having some the following keys:
|
||||||
|
|
||||||
|
:name:
|
||||||
|
|
||||||
|
The name of the alias to deprecate. (Required.)
|
||||||
|
|
||||||
|
:version:
|
||||||
|
|
||||||
|
The version of ansible-base or the collection this alias will be removed in. Either ``version`` or ``date`` must be specified.
|
||||||
|
|
||||||
|
:date:
|
||||||
|
|
||||||
|
The a date after which a minor release of ansible-base or a major collection release will no longer contain this alias.. Either ``version`` or ``date`` must be specified.
|
||||||
|
|
||||||
|
:collection_name:
|
||||||
|
|
||||||
|
Specifies which collection (or ansible-base) deprecates this deprecated alias. Specify ``ansible.builtin`` for ansible-base, or the collection's name (format ``foo.bar``). Must be used with ``version`` or ``date``.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
'option': {
|
||||||
|
'type': 'str',
|
||||||
|
'aliases': ['foo', 'bar'],
|
||||||
|
'depecated_aliases': [
|
||||||
|
{
|
||||||
|
'name': 'foo',
|
||||||
|
'version': '2.0.0',
|
||||||
|
'collection_name': 'testns.testcol',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'foo',
|
||||||
|
'date': '2020-12-31',
|
||||||
|
'collection_name': 'testns.testcol',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
:mutually_exclusive:
|
||||||
|
|
||||||
|
If ``options`` is specified, ``mutually_exclusive`` refers to the sub-options described in ``options`` and behaves as in :ref:`argument_spec_dependencies`.
|
||||||
|
|
||||||
|
:required_together:
|
||||||
|
|
||||||
|
If ``options`` is specified, ``required_together`` refers to the sub-options described in ``options`` and behaves as in :ref:`argument_spec_dependencies`.
|
||||||
|
|
||||||
|
:required_one_of:
|
||||||
|
|
||||||
|
If ``options`` is specified, ``required_one_of`` refers to the sub-options described in ``options`` and behaves as in :ref:`argument_spec_dependencies`.
|
||||||
|
|
||||||
|
:required_if:
|
||||||
|
|
||||||
|
If ``options`` is specified, ``required_if`` refers to the sub-options described in ``options`` and behaves as in :ref:`argument_spec_dependencies`.
|
||||||
|
|
||||||
|
:required_by:
|
||||||
|
|
||||||
|
If ``options`` is specified, ``required_by`` refers to the sub-options described in ``options`` and behaves as in :ref:`argument_spec_dependencies`.
|
||||||
|
|
||||||
|
|
||||||
|
.. _argument_spec_dependencies:
|
||||||
|
|
||||||
|
Dependencies between module options
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The following are optional arguments for ``AnsibleModule()``:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
module = AnsibleModule(
|
||||||
|
argument_spec,
|
||||||
|
mutually_exclusive=[
|
||||||
|
('path', 'content'),
|
||||||
|
],
|
||||||
|
required_one_of=[
|
||||||
|
('path', 'content'),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
:mutually_exclusive:
|
||||||
|
|
||||||
|
Must be a sequence (list or tuple) of sequences of strings. Every sequence of strings is a list of option names which are mutually exclusive. If more than one options of a list are specified together, Ansible will fail the module with an error.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
mutually_exclusive=[
|
||||||
|
('path', 'content'),
|
||||||
|
('repository_url', 'repository_filename'),
|
||||||
|
],
|
||||||
|
|
||||||
|
In this example, the options ``path`` and ``content`` must not specified at the same time. Also the options ``repository_url`` and ``repository_filename`` must not be specified at the same time. But specifying ``path`` and ``repository_url`` is accepted.
|
||||||
|
|
||||||
|
To ensure that precisely one of two (or more) options is specified, combine ``mutually_exclusive`` with ``required_one_of``.
|
||||||
|
|
||||||
|
:required_together:
|
||||||
|
|
||||||
|
Must be a sequence (list or tuple) of sequences of strings. Every sequence of strings is a list of option names which are must be specified together. If at least one of these options are specified, the other ones from the same sequence must all be present.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
required_together=[
|
||||||
|
('file_path', 'file_hash'),
|
||||||
|
],
|
||||||
|
|
||||||
|
In this example, if one of the options ``file_path`` or ``file_hash`` is specified, Ansible will fail the module with an error if the other one is not specified.
|
||||||
|
|
||||||
|
:required_one_of:
|
||||||
|
|
||||||
|
Must be a sequence (list or tuple) of sequences of strings. Every sequence of strings is a list of option names from which at least one must be specified. If none one of these options are specified, Ansible will fail module execution.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
required_one_of=[
|
||||||
|
('path', 'content'),
|
||||||
|
],
|
||||||
|
|
||||||
|
In this example, at least one of ``path`` and ``content`` must be specified. If none are specified, execution will fail. Specifying both is explicitly allowed; to prevent this, combine ``required_one_of`` with ``mutually_exclusive``.
|
||||||
|
|
||||||
|
:required_if:
|
||||||
|
|
||||||
|
Must be a sequence of sequences. Every inner sequence describes one conditional dependency. Every sequence must have three or four values. The first two values are the option's name and the option's value which describes the condition. The further elements of the sequence are only needed if the option of that name has precisely this value.
|
||||||
|
|
||||||
|
If you want that all options in a list of option names are specified if the condition is met, use one of the following forms:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
('option_name', option_value, ('option_a', 'option_b', ...)),
|
||||||
|
('option_name', option_value, ('option_a', 'option_b', ...), False),
|
||||||
|
|
||||||
|
If you want that at least one option of a list of option names is specified if the condition is met, use the following form:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
('option_name', option_value, ('option_a', 'option_b', ...), True),
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
required_if=[
|
||||||
|
('state', 'present', ('path', 'content'), True),
|
||||||
|
('force', True, ('force_reason', 'force_code')),
|
||||||
|
],
|
||||||
|
|
||||||
|
In this example, if the user specifies ``state=present``, at least one of the options ``path`` and ``content`` must be supplied (or both). To make sure that precisely one can be specified, combine ``required_if`` with ``mutually_exclusive``.
|
||||||
|
|
||||||
|
On the other hand, if ``force`` (a boolean parameter) is set to ``true``, ``yes`` etc., both ``force_reason`` and ``force_code`` must be specified.
|
||||||
|
|
||||||
|
:required_by:
|
||||||
|
|
||||||
|
Must be a dictionary mapping option names to sequences of option names. If the option name in a dictionary key is specified, the option names it maps to must all also be specified. Note that instead of a sequence of option names, you can also specify one single option name.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
required_by={
|
||||||
|
'force': 'force_reason',
|
||||||
|
'path': ('mode', 'owner', 'group'),
|
||||||
|
},
|
||||||
|
|
||||||
|
In the example, if ``force`` is specified, ``force_reason`` must also be specified. Also, if ``path`` is specified, then three three options ``mode``, ``owner`` and ``group`` also must be specified.
|
||||||
|
|
||||||
|
Declaring check mode support
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
To declare that a module supports check mode, supply ``supports_check_mode=True`` to the ``AnsibleModule()`` call:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec, supports_check_mode=True)
|
||||||
|
|
||||||
|
The module can determine whether it is called in check mode by checking the boolean value ``module.check_mode``. If it evaluates to ``True``, the module must take care not to do any modification.
|
||||||
|
|
||||||
|
If ``supports_check_mode=False`` is specified, which is the default value, the module will exit in check mode with ``skipped=True`` and message ``remote module (<insert module name here>) does not support check mode``.
|
||||||
|
|
||||||
|
Adding file options
|
||||||
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
To declare that a module should add support for all common file options, supply ``add_file_common_args=True`` to the ``AnsibleModule()`` call:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec, add_file_common_args=True)
|
||||||
|
|
||||||
|
You can find `a list of all file options here <https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/doc_fragments/files.py>`_. It is recommended that you make your ``DOCUMENTATION`` extend the doc fragment ``ansible.builtin.files`` (see :ref:`module_docs_fragments`) in this case, to make sure that all these fields are correctly documented.
|
||||||
|
|
||||||
|
The helper functions ``module.load_file_common_arguments()`` and ``module.set_fs_attributes_if_different()`` can be used to handle these arguments for you:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
argument_spec = {
|
||||||
|
'path': {
|
||||||
|
'type': 'str',
|
||||||
|
'required': True,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec, add_file_common_args=True)
|
||||||
|
changed = False
|
||||||
|
|
||||||
|
# TODO do something with module.params['path'], like update it's contents
|
||||||
|
|
||||||
|
# Ensure that module.params['path'] satisfies the file options supplied by the user
|
||||||
|
file_args = module.load_file_common_arguments(module.params)
|
||||||
|
changed = module.set_fs_attributes_if_different(file_args, changed)
|
||||||
|
|
||||||
|
module.exit_json(changed=changed)
|
||||||
|
|
Loading…
Reference in a new issue