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.
pull/72485/head
Felix Fontein 4 years ago committed by GitHub
parent 569d937df8
commit 01d207a3e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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.
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.
.. 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.
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.
.. literalinclude:: ../../../../examples/scripts/my_test.py

@ -561,83 +561,320 @@ Example ``argument_spec``:
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
* list
* dict
* bool
* int
* float
* path
* raw
* jsonarg
* json
* bytes
* bits
* str
* list
* dict
* bool
* int
* float
* path
* raw
* jsonarg
* json
* bytes
* 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::
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``.
``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.
.. 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:
``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:
``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:
``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.
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:
``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'),
],
)
aliases
"""""""
:mutually_exclusive:
``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``
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.
options
"""""""
Example:
``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.
.. 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
apply_defaults
""""""""""""""
argument_spec = {
'path': {
'type': 'str',
'required': True,
},
}
``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.
module = AnsibleModule(argument_spec, add_file_common_args=True)
changed = False
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.
# TODO do something with module.params['path'], like update it's contents
removed_in_version
""""""""""""""""""
# 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)
``removed_in_version`` indicates which version of Ansible a deprecated argument will be removed in.
module.exit_json(changed=changed)

Loading…
Cancel
Save