[2.12] 'attributes' (#73707)
* wip 'attributes' * added version added tests * syntzx * not bile * correztlys merges * moved desc to frag * simpler as dict * unused * clog * Update lib/ansible/utils/plugin_docs.py Co-authored-by: Jacob Floyd <cognifloyd@gmail.com> * unnoted * added action plugins * longer list * add sttri schema * huh? * itsdict * dictit * yolo * gnore for now * moar attribs * allow extras * positive * added loop, documented 'imports' * support is now none/partial/full * import_playbook is outside host loop Co-authored-by: Jacob Floyd <cognifloyd@gmail.com>
This commit is contained in:
parent
185d410316
commit
07939b04f3
9 changed files with 166 additions and 16 deletions
2
changelogs/fragments/plugin_attributes.yml
Normal file
2
changelogs/fragments/plugin_attributes.yml
Normal file
|
@ -0,0 +1,2 @@
|
|||
minor_changes:
|
||||
- ansible-doc now supports 'attributes' for plugins as per proposal.
|
|
@ -54,6 +54,7 @@ def jdump(text):
|
|||
try:
|
||||
display.display(json.dumps(text, cls=AnsibleJSONEncoder, sort_keys=True, indent=4))
|
||||
except TypeError as e:
|
||||
display.vvv(traceback.format_exc())
|
||||
raise AnsibleError('We could not convert all the documentation into JSON as there was a conversion issue: %s' % to_native(e))
|
||||
|
||||
|
||||
|
@ -780,7 +781,8 @@ class DocCLI(CLI, RoleMixin):
|
|||
try:
|
||||
text = DocCLI.get_man_text(doc, collection_name, plugin_type)
|
||||
except Exception as e:
|
||||
raise AnsibleError("Unable to retrieve documentation from '%s' due to: %s" % (plugin, to_native(e)))
|
||||
display.vvv(traceback.format_exc())
|
||||
raise AnsibleError("Unable to retrieve documentation from '%s' due to: %s" % (plugin, to_native(e)), orig_exc=e)
|
||||
|
||||
return text
|
||||
|
||||
|
@ -874,6 +876,7 @@ class DocCLI(CLI, RoleMixin):
|
|||
pfiles[plugin] = filename
|
||||
|
||||
except Exception as e:
|
||||
display.vvv(traceback.format_exc())
|
||||
raise AnsibleError("Failed reading docs at %s: %s" % (plugin, to_native(e)), orig_exc=e)
|
||||
|
||||
return pfiles
|
||||
|
@ -921,9 +924,7 @@ class DocCLI(CLI, RoleMixin):
|
|||
|
||||
@staticmethod
|
||||
def _dump_yaml(struct, indent):
|
||||
return DocCLI.tty_ify('\n'.join([indent + line for line in
|
||||
yaml.dump(struct, default_flow_style=False,
|
||||
Dumper=AnsibleDumper).split('\n')]))
|
||||
return DocCLI.tty_ify('\n'.join([indent + line for line in yaml.dump(struct, default_flow_style=False, Dumper=AnsibleDumper).split('\n')]))
|
||||
|
||||
@staticmethod
|
||||
def add_fields(text, fields, limit, opt_indent, return_values=False, base_indent=''):
|
||||
|
@ -1051,6 +1052,11 @@ class DocCLI(CLI, RoleMixin):
|
|||
DocCLI.add_fields(text, doc.pop('options'), limit, opt_indent)
|
||||
text.append('')
|
||||
|
||||
if doc.get('attributes'):
|
||||
text.append("ATTRIBUTES:\n")
|
||||
text.append(DocCLI._dump_yaml(doc.pop('attributes'), opt_indent))
|
||||
text.append('')
|
||||
|
||||
# generic elements we will handle identically
|
||||
for k in ('author',):
|
||||
if k not in doc:
|
||||
|
@ -1115,6 +1121,11 @@ class DocCLI(CLI, RoleMixin):
|
|||
DocCLI.add_fields(text, doc.pop('options'), limit, opt_indent)
|
||||
text.append('')
|
||||
|
||||
if doc.get('attributes', False):
|
||||
text.append("ATTRIBUTES:\n")
|
||||
text.append(DocCLI._dump_yaml(doc.pop('attributes'), opt_indent))
|
||||
text.append('')
|
||||
|
||||
if doc.get('notes', False):
|
||||
text.append("NOTES:")
|
||||
for note in doc['notes']:
|
||||
|
|
|
@ -121,9 +121,9 @@ extends_documentation_fragment:
|
|||
- decrypt
|
||||
- files
|
||||
- validate
|
||||
- action_common_attributes
|
||||
notes:
|
||||
- The M(ansible.builtin.copy) module recursively copy facility does not scale to lots (>hundreds) of files.
|
||||
- Supports C(check_mode).
|
||||
seealso:
|
||||
- module: ansible.builtin.assemble
|
||||
- module: ansible.builtin.fetch
|
||||
|
@ -134,6 +134,15 @@ seealso:
|
|||
author:
|
||||
- Ansible Core Team
|
||||
- Michael DeHaan
|
||||
attributes:
|
||||
action:
|
||||
support: full
|
||||
check_mode:
|
||||
version_added: '0.9'
|
||||
support: full
|
||||
diff_mode:
|
||||
support: full
|
||||
version_added: '0.9'
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
|
|
|
@ -22,6 +22,27 @@ options:
|
|||
free-form:
|
||||
description:
|
||||
- The name of the imported playbook is specified directly without any other option.
|
||||
extends_documentation_fragment:
|
||||
- action_common_attributes
|
||||
attributes:
|
||||
async:
|
||||
support: none
|
||||
become:
|
||||
support: none
|
||||
bypass_host_loop:
|
||||
support: full
|
||||
conditional:
|
||||
support: none
|
||||
connection:
|
||||
support: none
|
||||
delegation:
|
||||
support: none
|
||||
loops:
|
||||
support: none
|
||||
tags:
|
||||
support: none
|
||||
until:
|
||||
support: none
|
||||
notes:
|
||||
- This is a core feature of Ansible, rather than a module, and cannot be overridden like a module.
|
||||
seealso:
|
||||
|
|
|
@ -57,6 +57,27 @@ options:
|
|||
type: bool
|
||||
default: yes
|
||||
version_added: '2.11'
|
||||
extends_documentation_fragment:
|
||||
- action_common_attributes
|
||||
attributes:
|
||||
async:
|
||||
support: none
|
||||
become:
|
||||
support: none
|
||||
bypass_host_loop:
|
||||
support: partial
|
||||
conditional:
|
||||
support: none
|
||||
connection:
|
||||
support: none
|
||||
delegation:
|
||||
support: none
|
||||
loops:
|
||||
support: none
|
||||
tags:
|
||||
support: none
|
||||
until:
|
||||
support: none
|
||||
notes:
|
||||
- Handlers are made available to the whole play.
|
||||
- Since Ansible 2.7 variables defined in C(vars) and C(defaults) for the role are exposed to the play at playbook parsing time.
|
||||
|
|
|
@ -22,6 +22,27 @@ options:
|
|||
- The name of the imported file is specified directly without any other option.
|
||||
- Most keywords, including loops and conditionals, only applied to the imported tasks, not to this statement itself.
|
||||
- If you need any of those to apply, use M(ansible.builtin.include_tasks) instead.
|
||||
extends_documentation_fragment:
|
||||
- action_common_attributes
|
||||
attributes:
|
||||
async:
|
||||
support: none
|
||||
become:
|
||||
support: none
|
||||
bypass_host_loop:
|
||||
support: partial
|
||||
conditional:
|
||||
support: none
|
||||
connection:
|
||||
support: none
|
||||
delegation:
|
||||
support: none
|
||||
loops:
|
||||
support: none
|
||||
tags:
|
||||
support: none
|
||||
until:
|
||||
support: none
|
||||
notes:
|
||||
- This is a core feature of Ansible, rather than a module, and cannot be overridden like a module.
|
||||
seealso:
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2015, Ansible, Inc
|
||||
# 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
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
# Standard documentation fragment
|
||||
DOCUMENTATION = r'''
|
||||
attributes:
|
||||
action:
|
||||
description: Indicates this has a corresponding action plugin so some parts of the options can be executed on the controller
|
||||
support: none
|
||||
async:
|
||||
description: Supports being used with the ``async`` keyword
|
||||
support: full
|
||||
become:
|
||||
description: Is usable alongside become keywords
|
||||
support: full
|
||||
bypass_host_loop:
|
||||
description: Forces a 'global' task that does not execute per host, cannot be used in non lockstep strategies
|
||||
support: none
|
||||
check_mode:
|
||||
description: Can run in check_mode and return changed status prediction
|
||||
support: none
|
||||
connection:
|
||||
description: Uses the target's configured connection information to execute code on it
|
||||
support: full
|
||||
conditional:
|
||||
description: Will respect the `when` keyword per item loop or task (when no loop is present)
|
||||
support: full
|
||||
delegation:
|
||||
description: Can be used in conjunction with delegate_to and related keywords
|
||||
support: full
|
||||
diff:
|
||||
description: Will return details on what has changed when in diff is enabled
|
||||
support: none
|
||||
facts:
|
||||
description: Action returns an ``ansible_facts`` dictionary that will update existing host facts
|
||||
support: none
|
||||
loops:
|
||||
description: both ``loop`` and ``with_`` looping keywords will be honored.
|
||||
support: full
|
||||
proprietary:
|
||||
description: Can only be run against specific proprietary OS, normally a network appliance or similar
|
||||
support: none
|
||||
posix:
|
||||
description: Can be run against most POSIX (and GNU/Linux) OS targets
|
||||
support: full
|
||||
tags:
|
||||
description: Tags will determine if this task considered for execution
|
||||
support: full
|
||||
until:
|
||||
description: Usable inside until/retry loops
|
||||
support: full
|
||||
windows:
|
||||
description: Can be run against Windows OS targets
|
||||
support: none
|
||||
'''
|
|
@ -6,13 +6,14 @@ __metaclass__ = type
|
|||
|
||||
from ansible import constants as C
|
||||
from ansible.release import __version__ as ansible_version
|
||||
from ansible.errors import AnsibleError, AnsibleAssertionError
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils.six import string_types
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.common._collections_compat import MutableMapping, MutableSet, MutableSequence
|
||||
from ansible.parsing.plugin_docs import read_docstring
|
||||
from ansible.parsing.yaml.loader import AnsibleLoader
|
||||
from ansible.utils.display import Display
|
||||
from ansible.utils.vars import combine_vars
|
||||
|
||||
display = Display()
|
||||
|
||||
|
@ -179,17 +180,19 @@ def add_fragments(doc, filename, fragment_loader, is_module=False):
|
|||
doc['seealso'] = []
|
||||
doc['seealso'].extend(seealso)
|
||||
|
||||
if 'options' not in fragment:
|
||||
raise Exception("missing options in fragment (%s), possibly misformatted?: %s" % (fragment_name, filename))
|
||||
if 'options' not in fragment and 'attributes' not in fragment:
|
||||
raise Exception("missing options or attributes in fragment (%s), possibly misformatted?: %s" % (fragment_name, filename))
|
||||
|
||||
# ensure options themselves are directly merged
|
||||
if 'options' in doc:
|
||||
for doc_key in ['options', 'attributes']:
|
||||
if doc_key in fragment:
|
||||
if doc_key in doc:
|
||||
try:
|
||||
merge_fragment(doc['options'], fragment.pop('options'))
|
||||
merge_fragment(doc[doc_key], fragment.pop(doc_key))
|
||||
except Exception as e:
|
||||
raise AnsibleError("%s options (%s) of unknown type: %s" % (to_native(e), fragment_name, filename))
|
||||
raise AnsibleError("%s %s (%s) of unknown type: %s" % (to_native(e), doc_key, fragment_name, filename))
|
||||
else:
|
||||
doc['options'] = fragment.pop('options')
|
||||
doc[doc_key] = fragment.pop(doc_key)
|
||||
|
||||
# merge rest of the sections
|
||||
try:
|
||||
|
|
|
@ -522,7 +522,7 @@ def doc_schema(module_name, for_collection=False, deprecated_module=False):
|
|||
All(
|
||||
Schema(
|
||||
doc_schema_dict,
|
||||
extra=PREVENT_EXTRA
|
||||
extra=ALLOW_EXTRA
|
||||
),
|
||||
partial(version_added, error_code='module-invalid-version-added', accept_historical=not for_collection),
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue