diff --git a/doc/source/_exts/ansible-module-autodoc.py b/doc/source/_exts/ansible-module-autodoc.py new file mode 100644 index 000000000..aed0a8ca2 --- /dev/null +++ b/doc/source/_exts/ansible-module-autodoc.py @@ -0,0 +1,175 @@ +# Copyright 2019 Red Hat, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +import imp +import os + +from docutils import core +from docutils import nodes +from docutils.parsers.rst import Directive +from docutils.parsers import rst +from docutils.writers.html4css1 import Writer + +from sphinx import addnodes + +import yaml + + +class AnsibleAutoPluginDirective(Directive): + directive_name = "ansibleautoplugin" + has_content = True + option_spec = { + 'module': rst.directives.unchanged_required, + 'documentation': rst.directives.unchanged, + 'examples': rst.directives.unchanged + } + + @staticmethod + def _render_html(source): + return core.publish_parts( + source=source, + writer=Writer(), + writer_name='html', + settings_overrides={'no_system_messages': True} + ) + + def make_node(self, title, contents, content_type=None): + section = nodes.section( + title, + nodes.title(text=title), + ids=[nodes.make_id(__file__)], + ) + + if not content_type: + # Doc section + for content in contents['docs']: + for paragraph in content.split('\n'): + retnode = nodes.paragraph() + html = self._render_html(source=paragraph) + retnode += nodes.raw('', html['body'], format='html') + section.append(retnode) + + # Options Section + options_list = nodes.field_list() + options_section = nodes.section( + 'Options', + nodes.title(text='Options'), + ids=[nodes.make_id(__file__)], + ) + for key, value in contents['options'].items(): + body = nodes.field_body() + if isinstance(value['description'], list): + for desc in value['description']: + html = self._render_html(source=desc) + body.append( + nodes.raw('', html['body'], format='html') + ) + else: + html = self._render_html(source=value['description']) + body.append( + nodes.raw('', html['body'], format='html') + ) + + field = nodes.field() + field.append(nodes.field_name(text=key)) + field.append(body) + options_list.append(field) + else: + options_section.append(options_list) + section.append(options_section) + + # Authors Section + authors_list = nodes.field_list() + authors_section = nodes.section( + 'Authors', + nodes.title(text='Authors'), + ids=[nodes.make_id(__file__)], + ) + field = nodes.field() + field.append(nodes.field_name(text='')) + for author in contents['author']: + body = nodes.field_body() + html = self._render_html(source=author) + body.append( + nodes.raw('', html['body'], format='html') + ) + field.append(body) + else: + authors_list.append(field) + authors_section.append(authors_list) + section.append(authors_section) + + elif content_type == 'yaml': + for content in contents: + retnode = nodes.literal_block(text=content) + retnode['language'] = 'yaml' + section.append(retnode) + + return section + + def load_module(self, filename): + return imp.load_source('__ansible_module__', filename) + + def build_documentation(self, module): + docs = yaml.safe_load(module.DOCUMENTATION) + doc_data = dict() + doc_data['docs'] = docs['description'] + doc_data['author'] = docs.get('author', list()) + doc_data['options'] = docs.get('options', dict()) + return doc_data + + def build_examples(self, module): + examples = yaml.safe_load(module.EXAMPLES) + return_examples = list() + for example in examples: + return_examples.append( + yaml.safe_dump([example], default_flow_style=False) + ) + return return_examples + + def run(self): + module = self.load_module(filename=self.options['module']) + return_data = list() + if self.options.get('documentation'): + docs = self.build_documentation(module=module) + return_data.append( + self.make_node( + title="Module Documentation", + contents=docs + ) + ) + + if self.options.get('examples'): + examples = self.build_examples(module=module) + return_data.append( + self.make_node( + title="Example Tasks", + contents=examples, + content_type='yaml' + ) + ) + + return return_data + + +def setup(app): + classes = [ + AnsibleAutoPluginDirective, + ] + for directive_class in classes: + app.add_directive(directive_class.directive_name, directive_class) + + return {'version': '0.1'} diff --git a/doc/source/conf.py b/doc/source/conf.py index f28675b82..4cf145cc6 100755 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -26,12 +26,14 @@ sys.path.insert(0, os.path.join(os.path.abspath('.'), '_exts')) # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'openstackdocstheme', - 'sphinx.ext.autodoc' + 'sphinx.ext.autodoc', + 'ansible-module-autodoc' ] # autodoc generation is a bit aggressive and a nuisance when doing heavy # text edit cycles. # execute "export SPHINX_DEBUG=1" in your terminal to disable +# autodoc_mock_imports = ["django"] # The suffix of source filenames. source_suffix = '.rst' diff --git a/doc/source/contributing.rst b/doc/source/contributing.rst index a74d2408f..45e944945 100644 --- a/doc/source/contributing.rst +++ b/doc/source/contributing.rst @@ -144,3 +144,34 @@ test being executed and running the environment. .. code-block:: console (test-python) $ molecule --debug test + + +Contributing plugins +~~~~~~~~~~~~~~~~~~~~ + +All plugins contributed to the TripleO-Ansible can be found in the +`tripleo_ansible/ansible_plugins` directory, from the root of this project. +When contributing a plugin, make sure to also add documentation in the +`doc/source/modules` folder. All documentation added to this folder will be +automatically indexed and rendered via `sphinx`. + +If a contributed plugin is following the Ansible practice of placing +documentation within the plugin itself, the following snippet can be used in a +sphinx template to auto-render the in-code documentation. + +.. code-block:: rst + + .. ansibleautoplugin:: + :module: tripleo_ansible/ansible_plugins/${DIRECTORY}/${PLUGINFILE} + :documentation: true + :examples: true + +The snippet can take two options, `documentation` and `examples`. If a given +plugin does not have either of these in-code documentation objects, +documentation for either type can be disabled by omitting the option. + +.. code-block:: rst + + .. ansibleautoplugin:: + :module: tripleo_ansible/ansible_plugins/${DIRECTORY}/${PLUGINFILE} + :documentation: true diff --git a/doc/source/index.rst b/doc/source/index.rst index 2e9c5283c..2c31afc1c 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -15,6 +15,7 @@ Contents: contributing usage roles + modules Indices and tables ================== diff --git a/doc/source/modules.rst b/doc/source/modules.rst new file mode 100644 index 000000000..8de930115 --- /dev/null +++ b/doc/source/modules.rst @@ -0,0 +1,9 @@ +Documented modules in TripleO-Ansible +===================================== + +Contents: + +.. toctree:: + :glob: + + modules/* diff --git a/doc/source/modules/action-package.rst b/doc/source/modules/action-package.rst new file mode 100644 index 000000000..39425bcb3 --- /dev/null +++ b/doc/source/modules/action-package.rst @@ -0,0 +1,14 @@ +================ +Module - package +================ + + +This module provides for the following ansible plugin: + + * package + + +.. ansibleautoplugin:: + :module: tripleo_ansible/ansible_plugins/action/package.py + :documentation: true + :examples: true diff --git a/doc/source/modules/calllback-json_error.rst b/doc/source/modules/calllback-json_error.rst new file mode 100644 index 000000000..f8439ae03 --- /dev/null +++ b/doc/source/modules/calllback-json_error.rst @@ -0,0 +1,13 @@ +=================== +Module - json_error +=================== + + +This module provides for the following ansible plugin: + + * json_error + + +.. ansibleautoplugin:: + :module: tripleo_ansible/ansible_plugins/callback/json_error.py + :documentation: true diff --git a/doc/source/modules/modules-podman_container.rst b/doc/source/modules/modules-podman_container.rst new file mode 100644 index 000000000..025456dd5 --- /dev/null +++ b/doc/source/modules/modules-podman_container.rst @@ -0,0 +1,14 @@ +========================= +Module - podman_container +========================= + + +This module provides for the following ansible plugin: + + * podman_container + + +.. ansibleautoplugin:: + :module: tripleo_ansible/ansible_plugins/modules/podman_container.py + :documentation: true + :examples: true diff --git a/doc/source/modules/modules-podman_image.rst b/doc/source/modules/modules-podman_image.rst new file mode 100644 index 000000000..8afa98080 --- /dev/null +++ b/doc/source/modules/modules-podman_image.rst @@ -0,0 +1,14 @@ +===================== +Module - podman_image +===================== + + +This module provides for the following ansible plugin: + + * podman_image + + +.. ansibleautoplugin:: + :module: tripleo_ansible/ansible_plugins/modules/podman_image.py + :documentation: true + :examples: false diff --git a/doc/source/modules/modules-podman_image_facts.rst b/doc/source/modules/modules-podman_image_facts.rst new file mode 100644 index 000000000..27f7da68c --- /dev/null +++ b/doc/source/modules/modules-podman_image_facts.rst @@ -0,0 +1,14 @@ +=========================== +Module - podman_image_facts +=========================== + + +This module provides for the following ansible plugin: + + * podman_image_facts + + +.. ansibleautoplugin:: + :module: tripleo_ansible/ansible_plugins/modules/podman_image_facts.py + :documentation: true + :examples: true diff --git a/tox.ini b/tox.ini index 65262ede9..e76528cdc 100644 --- a/tox.ini +++ b/tox.ini @@ -95,10 +95,12 @@ commands = [testenv:docs] basepython = python3 -deps = -r{toxinidir}/doc/requirements.txt +deps = + -r{toxinidir}/doc/requirements.txt + -r{toxinidir}/molecule-requirements.txt commands= doc8 doc - sphinx-build -a -E -W -d doc/build/doctrees -b html doc/source doc/build/html + sphinx-build -a -E -W -d doc/build/doctrees -b html doc/source doc/build/html -T [doc8] # Settings for doc8: diff --git a/tripleo_ansible/ansible_plugins/action/package.py b/tripleo_ansible/ansible_plugins/action/package.py index 6cb3647d0..d56f74f29 100644 --- a/tripleo_ansible/ansible_plugins/action/package.py +++ b/tripleo_ansible/ansible_plugins/action/package.py @@ -20,6 +20,61 @@ import os import ansible.plugins.action as action +DOCUMENTATION = """ +--- +module: package +author: + - Kevin Carter (@cloudnull) +version_added: '2.8' +short_description: Tripleo action plugin to evaluate package installations +notes: [] +description: + - This is an action plugin shim that will intercept the use of + the standard package module. The intention of this shim is to ensure the + package module respects the option `tripleo_enable_package_install` + which is used to control the installation of packages through a + deployment. + + This plugin will do nothing if `tripleo_enable_package_install` + is unset thereby allowing ansible to function normally. When the global + option is present the plugin will evaluate its truthiness and react + accordingly. + + * False - No action taken, task will be marked as skipped. + + * True - Package installation happens normally. + + If this module encounters an error while processesing the module will + proceed as if the option `tripleo_enable_package_install` is unset which + ensures ansible tasks are handled correctly no matter the context in + which they are executed. + + Anytime this module results in a "skip" a message will be made available + which indicates why it was skipped. Messages will only be visualized + when debug mode has been enabled or through registering a variable and + using it a task which can print messages; e.g. `debug` or `fail`. +options: + tripleo_enable_package_install: + description: + - Boolean option to enable or disable package installations. This option + can be passed in as a task var, groupvar, or hostvar. This option is + **NOT** a module argument. + required: True + default: True +""" + + +EXAMPLES = """ +# Run package install +- name: Run Package Installation + package: + name: mypackage + state: present + vars: + tripleo_enable_package_install: true +""" + + # NOTE(cloudnull): imp is being used because core action plugins are not # importable in py27. Once we get to the point where we # no longer support py27 these lines should be converted @@ -63,32 +118,6 @@ def _bool_set(bool_opt): class ActionModule(PKG.ActionModule): - """Tripleo action plugin made to evaluate package installations. - - This is an action plugin shim that will intercept the use of - the standard package module. The intention of this shim is to ensure the - package module respects the option `tripleo_enable_package_install` - which is used to control the installation of packages through a deployment. - - This plugin will do nothing if the option `tripleo_enable_package_install` - is unset thereby allowing ansible to function normally. When the global - option is present the plugin will evaluate its truthiness and react - accordingly. - - * False: No action taken, task will be marked as skipped. - * True: Package installation happens normally. - - If this module encounters an error while processesing the module will - proceed as if the option `tripleo_enable_package_install` is unset which - ensures ansible tasks are handled correctly no matter the context in which - they are executed. - - Anytime this module results in a "skip" a message will be made available - which indicates why it was skipped. Messages will only be visualized when - debug mode has been enabled or through registering a variable and using it - a task which can print messages; e.g. `debug` or `fail`. - """ - def run(self, tmp=None, task_vars=None): """Shim for tripleo package operations. diff --git a/tripleo_ansible/ansible_plugins/modules/podman_container.py b/tripleo_ansible/ansible_plugins/modules/podman_container.py index 28fd28bcb..963aa25dd 100644 --- a/tripleo_ansible/ansible_plugins/modules/podman_container.py +++ b/tripleo_ansible/ansible_plugins/modules/podman_container.py @@ -55,7 +55,7 @@ options: restart: description: - Use with started state to force a matching container to be stopped - and restarted. + and restarted. """ EXAMPLES = """