Browse code

generate rst doc pages for command line tools (#27530)

* let generate_man also gen rst pages for cli tools
* make template-file, output-dir, output format cli options for generate_man
* update main Makefile to use generate_man.py for docs (man pages and rst)
* update vault docs that use :option:
* Edits based on
https://github.com/alikins/ansible/commit/6e34ea62429c417939bd96b6de5bf7e6ab1ff765 and
https://github.com/alikins/ansible/commit/a3afc785357878da354e21a709cf3b9f16c3f146

* add a optparse 'desc' to lib/ansible/cli/config.py

The man page needs a short desc for the 'NAME' field
which it gets from the option parse 'desc' value.

Fixes building ansible-config man page.

* add trim_docstring from pep257 to generate_man

use pep258 docstring trim function to fix up any indention
weirdness inherit to doc strings (ie, lines other than
first line being indented.

* Add refs to cli command actions

To reference ansible-vaults --vault-id option, use:

:option:`The link text here <ansible-vault --vault-id>`

or:

:option:`--vault-id <ansible-vault --vault-id>`

To reference ansible-vault's 'encrypt' action, use:

:ref:`The link text here <ansible_vault_encrypt>`

or most of the time:

:ref:`ansible-vault encrypt <ansible_vault_encrypt>`
(cherry picked from commit 89c973445c6bbb08551703917a4eec6e484a3e0c)

Adrian Likins authored on 2017/09/08 04:44:20
Showing 9 changed files
... ...
@@ -29,6 +29,7 @@ ASCII2HTMLMAN = a2x -L -D docs/html/man/ -d manpage -f xhtml
29 29
 else
30 30
 ASCII2MAN = @echo "ERROR: AsciiDoc 'a2x' command is not installed but is required to build $(MANPAGES)" && exit 1
31 31
 endif
32
+GENERATE_CLI = docs/bin/generate_man.py
32 33
 
33 34
 PYTHON=python
34 35
 SITELIB = $(shell $(PYTHON) -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")
... ...
@@ -343,8 +344,9 @@ webdocs:
343 343
 
344 344
 .PHONY: generate_asciidoc
345 345
 generate_asciidoc: lib/ansible/cli/*.py
346
-	mkdir -p ./docs/man/man1/
347
-	PYTHONPATH=./lib ./docs/bin/generate_man.py
346
+	mkdir -p ./docs/man/man1/ ; \
347
+	PYTHONPATH=./lib $(GENERATE_CLI) --template-file=docs/templates/man.j2 --output-dir=docs/man/man1/ --output-format man lib/ansible/cli/*.py
348
+
348 349
 
349 350
 docs: generate_asciidoc
350 351
 	make $(MANPAGES)
... ...
@@ -1,6 +1,8 @@
1 1
 #!/usr/bin/env python
2 2
 
3
+import optparse
3 4
 import os
5
+import pprint
4 6
 import sys
5 7
 
6 8
 from jinja2 import Environment, FileSystemLoader
... ...
@@ -8,6 +10,46 @@ from jinja2 import Environment, FileSystemLoader
8 8
 from ansible.module_utils._text import to_bytes
9 9
 
10 10
 
11
+def generate_parser():
12
+    p = optparse.OptionParser(
13
+        version='%prog 1.0',
14
+        usage='usage: %prog [options]',
15
+        description='Generate cli documentation from cli docstrings',
16
+    )
17
+
18
+    p.add_option("-t", "--template-file", action="store", dest="template_file", default="../templates/man.j2", help="path to jinja2 template")
19
+    p.add_option("-o", "--output-dir", action="store", dest="output_dir", default='/tmp/', help="Output directory for rst files")
20
+    p.add_option("-f", "--output-format", action="store", dest="output_format", default='man', help="Output format for docs (the default 'man' or 'rst')")
21
+    return p
22
+
23
+
24
+# from https://www.python.org/dev/peps/pep-0257/
25
+def trim_docstring(docstring):
26
+    if not docstring:
27
+        return ''
28
+    # Convert tabs to spaces (following the normal Python rules)
29
+    # and split into a list of lines:
30
+    lines = docstring.expandtabs().splitlines()
31
+    # Determine minimum indentation (first line doesn't count):
32
+    indent = sys.maxint
33
+    for line in lines[1:]:
34
+        stripped = line.lstrip()
35
+        if stripped:
36
+            indent = min(indent, len(line) - len(stripped))
37
+    # Remove indentation (first line is special):
38
+    trimmed = [lines[0].strip()]
39
+    if indent < sys.maxint:
40
+        for line in lines[1:]:
41
+            trimmed.append(line[indent:].rstrip())
42
+    # Strip off trailing and leading blank lines:
43
+    while trimmed and not trimmed[-1]:
44
+        trimmed.pop()
45
+    while trimmed and not trimmed[0]:
46
+        trimmed.pop(0)
47
+    # Return a single string:
48
+    return '\n'.join(trimmed)
49
+
50
+
11 51
 def get_options(optlist):
12 52
     ''' get actual options '''
13 53
 
... ...
@@ -24,107 +66,215 @@ def get_options(optlist):
24 24
     return opts
25 25
 
26 26
 
27
+def get_option_groups(option_parser):
28
+    groups = []
29
+    for option_group in option_parser.option_groups:
30
+        group_info = {}
31
+        group_info['desc'] = option_group.get_description()
32
+        group_info['options'] = option_group.option_list
33
+        group_info['group_obj'] = option_group
34
+        groups.append(group_info)
35
+    return groups
36
+
37
+
27 38
 def opt_doc_list(cli):
28 39
     ''' iterate over options lists '''
29 40
 
30 41
     results = []
31
-    for optg in cli.parser.option_groups:
32
-        results.extend(get_options(optg.option_list))
42
+    for option_group in cli.parser.option_groups:
43
+        results.extend(get_options(option_group.option_list))
33 44
 
34 45
     results.extend(get_options(cli.parser.option_list))
35 46
 
36 47
     return results
37 48
 
38 49
 
39
-def opts_docs(cli, name):
50
+# def opts_docs(cli, name):
51
+def opts_docs(cli_class_name, cli_module_name):
40 52
     ''' generate doc structure from options '''
41 53
 
42
-    # cli name
43
-    if '-' in name:
44
-        name = name.split('-')[1]
45
-    else:
46
-        name = 'adhoc'
54
+    cli_name = 'ansible-%s' % cli_module_name
55
+    if cli_module_name == 'adhoc':
56
+        cli_name = 'ansible'
57
+
58
+    # WIth no action/subcommand
59
+    # shared opts set
60
+    # instantiate each cli and ask its options
61
+    cli_klass = getattr(__import__("ansible.cli.%s" % cli_module_name,
62
+                                   fromlist=[cli_class_name]), cli_class_name)
63
+    cli = cli_klass([])
64
+
65
+    # parse the common options
66
+    try:
67
+        cli.parse()
68
+    except:
69
+        pass
47 70
 
48
-    # cli info
71
+    # base/common cli info
49 72
     docs = {
50
-        'cli': name,
73
+        'cli': cli_module_name,
74
+        'cli_name': cli_name,
51 75
         'usage': cli.parser.usage,
52 76
         'short_desc': cli.parser.description,
53
-        'long_desc': cli.__doc__,
77
+        'long_desc': trim_docstring(cli.__doc__),
78
+        'actions': {},
54 79
     }
80
+    option_info = {'option_names': [],
81
+                   'options': [],
82
+                   'groups': []}
83
+
84
+    for extras in ('ARGUMENTS'):
85
+        if hasattr(cli, extras):
86
+            docs[extras.lower()] = getattr(cli, extras)
87
+
88
+    common_opts = opt_doc_list(cli)
89
+    groups_info = get_option_groups(cli.parser)
90
+    shared_opt_names = []
91
+    for opt in common_opts:
92
+        shared_opt_names.extend(opt.get('options', []))
93
+
94
+    option_info['options'] = common_opts
95
+    option_info['option_names'] = shared_opt_names
96
+
97
+    option_info['groups'].extend(groups_info)
98
+
99
+    docs.update(option_info)
55 100
 
101
+    # now for each action/subcommand
56 102
     # force populate parser with per action options
57
-    if cli.VALID_ACTIONS:
58
-        docs['actions'] = {}
103
+
104
+    # use class attrs not the attrs on a instance (not that it matters here...)
105
+    for action in getattr(cli_klass, 'VALID_ACTIONS', ()):
106
+        # instantiate each cli and ask its options
107
+        action_cli_klass = getattr(__import__("ansible.cli.%s" % cli_module_name,
108
+                                              fromlist=[cli_class_name]), cli_class_name)
109
+        # init with args with action added?
110
+        cli = action_cli_klass([])
111
+        cli.args.append(action)
112
+
113
+        try:
114
+            cli.parse()
115
+        except:
116
+            pass
117
+
118
+        # FIXME/TODO: needed?
59 119
         # avoid dupe errors
60 120
         cli.parser.set_conflict_handler('resolve')
61
-        for action in cli.VALID_ACTIONS:
62
-            cli.args.append(action)
63
-            cli.set_action()
64
-            docs['actions'][action] = getattr(cli, 'execute_%s' % action).__doc__
65 121
 
66
-    docs['options'] = opt_doc_list(cli)
122
+        cli.set_action()
123
+
124
+        action_info = {'option_names': [],
125
+                       'options': []}
126
+        # docs['actions'][action] = {}
127
+        # docs['actions'][action]['name'] = action
128
+        action_info['name'] = action
129
+        action_info['desc'] = trim_docstring(getattr(cli, 'execute_%s' % action).__doc__)
130
+
131
+        # docs['actions'][action]['desc'] = getattr(cli, 'execute_%s' % action).__doc__.strip()
132
+        action_doc_list = opt_doc_list(cli)
133
+
134
+        uncommon_options = []
135
+        for action_doc in action_doc_list:
136
+            # uncommon_options = []
67 137
 
138
+            option_aliases = action_doc.get('options', [])
139
+            for option_alias in option_aliases:
140
+
141
+                if option_alias in shared_opt_names:
142
+                    continue
143
+
144
+                # TODO: use set
145
+                if option_alias not in action_info['option_names']:
146
+                    action_info['option_names'].append(option_alias)
147
+
148
+                if action_doc in action_info['options']:
149
+                    continue
150
+
151
+                uncommon_options.append(action_doc)
152
+
153
+            action_info['options'] = uncommon_options
154
+
155
+        docs['actions'][action] = action_info
156
+
157
+    docs['options'] = opt_doc_list(cli)
68 158
     return docs
69 159
 
160
+
70 161
 if __name__ == '__main__':
71 162
 
72
-    template_file = 'man.j2'
163
+    parser = generate_parser()
164
+
165
+    options, args = parser.parse_args()
166
+
167
+    template_file = options.template_file
168
+    template_path = os.path.expanduser(template_file)
169
+    template_dir = os.path.abspath(os.path.dirname(template_path))
170
+    template_basename = os.path.basename(template_file)
73 171
 
172
+    output_dir = os.path.abspath(options.output_dir)
173
+    output_format = options.output_format
174
+
175
+    cli_modules = args
176
+
177
+    # various cli parsing things checks sys.argv if the 'args' that are passed in are []
178
+    # so just remove any args so the cli modules dont try to parse them resulting in warnings
179
+    sys.argv = [sys.argv[0]]
74 180
     # need to be in right dir
75 181
     os.chdir(os.path.dirname(__file__))
76 182
 
77 183
     allvars = {}
78 184
     output = {}
79 185
     cli_list = []
80
-    for binary in os.listdir('../../lib/ansible/cli'):
186
+    cli_bin_name_list = []
187
+
188
+    # for binary in os.listdir('../../lib/ansible/cli'):
189
+    for cli_module_name in cli_modules:
190
+        binary = os.path.basename(os.path.expanduser(cli_module_name))
81 191
 
82 192
         if not binary.endswith('.py'):
83 193
             continue
84 194
         elif binary == '__init__.py':
85 195
             continue
86 196
 
87
-        libname = os.path.splitext(binary)[0]
88
-        print("Found CLI %s" % libname)
197
+        cli_name = os.path.splitext(binary)[0]
89 198
 
90
-        if libname == 'adhoc':
91
-            myclass = 'AdHocCLI'
92
-            output[libname] = 'ansible.1.asciidoc.in'
199
+        if cli_name == 'adhoc':
200
+            cli_class_name = 'AdHocCLI'
201
+            # myclass = 'AdHocCLI'
202
+            output[cli_name] = 'ansible.1.asciidoc.in'
203
+            cli_bin_name = 'ansible'
93 204
         else:
94
-            myclass = "%sCLI" % libname.capitalize()
95
-            output[libname] = 'ansible-%s.1.asciidoc.in' % libname
205
+            # myclass = "%sCLI" % libname.capitalize()
206
+            cli_class_name = "%sCLI" % cli_name.capitalize()
207
+            output[cli_name] = 'ansible-%s.1.asciidoc.in' % cli_name
208
+            cli_bin_name = 'ansible-%s' % cli_name
96 209
 
97
-        # instantiate each cli and ask its options
98
-        mycli = getattr(__import__("ansible.cli.%s" % libname, fromlist=[myclass]), myclass)
99
-        cli_object = mycli([])
100
-        try:
101
-            cli_object.parse()
102
-        except:
103
-            # no options passed, we expect errors
104
-            pass
210
+        # FIXME:
211
+        allvars[cli_name] = opts_docs(cli_class_name, cli_name)
212
+        cli_bin_name_list.append(cli_bin_name)
105 213
 
106
-        allvars[libname] = opts_docs(cli_object, libname)
214
+    cli_list = allvars.keys()
107 215
 
108
-        for extras in ('ARGUMENTS'):
109
-            if hasattr(cli_object, extras):
110
-                allvars[libname][extras.lower()] = getattr(cli_object, extras)
216
+    doc_name_formats = {'man': '%s.1.asciidoc.in',
217
+                        'rst': '%s.rst'}
111 218
 
112
-    cli_list = allvars.keys()
113
-    for libname in cli_list:
219
+    for cli_name in cli_list:
114 220
 
115 221
         # template it!
116
-        env = Environment(loader=FileSystemLoader('../templates'))
117
-        template = env.get_template('man.j2')
222
+        env = Environment(loader=FileSystemLoader(template_dir))
223
+        template = env.get_template(template_basename)
118 224
 
119 225
         # add rest to vars
120
-        tvars = allvars[libname]
226
+        tvars = allvars[cli_name]
121 227
         tvars['cli_list'] = cli_list
122
-        tvars['cli'] = libname
228
+        tvars['cli_bin_name_list'] = cli_bin_name_list
229
+        tvars['cli'] = cli_name
123 230
         if '-i' in tvars['options']:
124 231
             print('uses inventory')
125 232
 
126 233
         manpage = template.render(tvars)
127
-        filename = '../man/man1/%s' % output[libname]
234
+        filename = os.path.join(output_dir, doc_name_formats[output_format] % tvars['cli_name'])
235
+
128 236
         with open(filename, 'wb') as f:
129 237
             f.write(to_bytes(manpage))
130
-            print("Wrote man docs to %s" % filename)
238
+            print("Wrote doc to %s" % filename)
... ...
@@ -4,6 +4,7 @@ FORMATTER=../bin/plugin_formatter.py
4 4
 TESTING_FORMATTER=../bin/testing_formatter.sh
5 5
 DUMPER=../bin/dump_keywords.py
6 6
 CONFIG_DUMPER=../bin/dump_config.py
7
+GENERATE_CLI=../bin/generate_man.py
7 8
 ifeq ($(shell echo $(OS) | egrep -ic 'Darwin|FreeBSD|OpenBSD|DragonFly'),1)
8 9
 CPUS ?= $(shell sysctl hw.ncpu|awk '{print $$2}')
9 10
 else
... ...
@@ -19,7 +20,8 @@ all: docs
19 19
 
20 20
 docs: clean htmldocs
21 21
 
22
-htmldocs: testing keywords modules staticmin config
22
+htmldocs: testing keywords modules staticmin cli config
23
+
23 24
 	CPUS=$(CPUS) $(MAKE) -f Makefile.sphinx html
24 25
 
25 26
 webdocs: docs
... ...
@@ -45,9 +47,14 @@ clean:
45 45
 	-rm rst/*_maintained.rst
46 46
 	-rm rst/playbooks_directives.rst
47 47
 	-rm rst/playbooks_keywords.rst
48
+#	-rm rst/cli/ansible*.rst
48 49
 
49 50
 .PHONEY: docs clean
50 51
 
52
+# TODO: make generate_man output dir cli option
53
+cli: $(GENERATE_CLI)
54
+	PYTHONPATH=../../lib $(GENERATE_CLI) --template-file=../templates/cli_rst.j2 --output-dir=rst/ --output-format rst ../../lib/ansible/cli/*.py
55
+
51 56
 keywords: $(FORMATTER) ../templates/playbooks_keywords.rst.j2
52 57
 	PYTHONPATH=../../lib $(DUMPER) --template-dir=../templates --output-dir=rst/ -d ./keyword_desc.yml
53 58
 
... ...
@@ -57,7 +64,7 @@ config:
57 57
 modules: $(FORMATTER) ../templates/plugin.rst.j2
58 58
 # Limit building of module documentation if requested.
59 59
 ifdef MODULES
60
-	PYTHONPATH=../../lib $(FORMATTER) -t rst --template-dir=../templates --module-dir=../../lib/ansible/modules -o rst/ -l $(MODULES)	
60
+	PYTHONPATH=../../lib $(FORMATTER) -t rst --template-dir=../templates --module-dir=../../lib/ansible/modules -o rst/ -l $(MODULES)
61 61
 else
62 62
 	PYTHONPATH=../../lib $(FORMATTER) -t rst --template-dir=../templates --module-dir=../../lib/ansible/modules -o rst/
63 63
 endif
64 64
new file mode 100644
... ...
@@ -0,0 +1,15 @@
0
+Command Line Tools
1
+==================
2
+
3
+
4
+.. toctree:: :maxdepth: 1
5
+
6
+    ansible
7
+    ansible-playbook
8
+    ansible-vault
9
+    ansible-galaxy
10
+    ansible-console
11
+    ansible-config
12
+    ansible-doc
13
+    ansible-inventory
14
+    ansible-pull
... ...
@@ -32,6 +32,7 @@ Ansible, Inc. releases a new major release of Ansible approximately every two mo
32 32
    modules
33 33
    modules_by_category
34 34
    vault
35
+   command_line_tools
35 36
    guides
36 37
    dev_guide/index
37 38
    tower
... ...
@@ -5,7 +5,7 @@ Ansible Vault
5 5
 
6 6
 New in Ansible 1.5, "Vault" is a feature of ansible that allows keeping sensitive data such as passwords or keys in encrypted files, rather than as plaintext in your playbooks or roles. These vault files can then be distributed or placed in source control.
7 7
 
8
-To enable this feature, a command line tool, :ref:`ansible-vault` is used to edit files, and a command line flag `--ask-vault-pass` or `--vault-password-file` is used. Alternately, you may specify the location of a password file or command Ansible to always prompt for the password in your ansible.cfg file. These options require no command line flag usage.
8
+To enable this feature, a command line tool - :ref:`ansible-vault` - is used to edit files, and a command line flag (:option:`--ask-vault-pass <ansible-playbook --ask-vault-pass>` or :option:`--vault-password-file <ansible-playbook --vault-password-file>`) is used. Alternately, you may specify the location of a password file or command Ansible to always prompt for the password in your ansible.cfg file. These options require no command line flag usage.
9 9
 
10 10
 For best practices advice, refer to :ref:`best_practices_for_variables_and_vaults`.
11 11
 
... ...
@@ -48,7 +48,7 @@ The default cipher is AES (which is shared-secret based).
48 48
 Editing Encrypted Files
49 49
 ```````````````````````
50 50
 
51
-To edit an encrypted file in place, use the :ref:`ansible-vault edit` command.
51
+To edit an encrypted file in place, use the :ref:`ansible-vault edit <ansible_vault_edit>` command.
52 52
 This command will decrypt the file to a temporary file and allow you to edit
53 53
 the file, saving it back when done and removing the temporary file:
54 54
 
... ...
@@ -78,7 +78,7 @@ Encrypting Unencrypted Files
78 78
 ````````````````````````````
79 79
 
80 80
 If you have existing files that you wish to encrypt, use
81
-the :ref:`ansible-vault encrypt` command.  This command can operate on multiple files at once:
81
+the :ref:`ansible-vault encrypt <ansible_vault_encrypt>` command.  This command can operate on multiple files at once:
82 82
 
83 83
 .. code-block:: bash
84 84
 
... ...
@@ -91,8 +91,8 @@ Decrypting Encrypted Files
91 91
 ``````````````````````````
92 92
 
93 93
 If you have existing files that you no longer want to keep encrypted, you can permanently decrypt
94
-them by running the :ref:`ansible-vault decrypt` command.  This command will save them unencrypted
95
-to the disk, so be sure you do not want :ref:`ansible-vault edit` instead:
94
+them by running the :ref:`ansible-vault decrypt <ansible_vault_decrypt>` command.  This command will save them unencrypted
95
+to the disk, so be sure you do not want :ref:`ansible-vault edit <ansible_vault_edit>` instead:
96 96
 
97 97
 .. code-block:: bash
98 98
 
... ...
@@ -106,7 +106,7 @@ Viewing Encrypted Files
106 106
 
107 107
 *Available since Ansible 1.8*
108 108
 
109
-If you want to view the contents of an encrypted file without editing it, you can use the :ref:`ansible-vault view` command:
109
+If you want to view the contents of an encrypted file without editing it, you can use the :ref:`ansible-vault view <ansible_vault_view>` command:
110 110
 
111 111
 .. code-block:: bash
112 112
 
... ...
@@ -118,10 +118,11 @@ If you want to view the contents of an encrypted file without editing it, you ca
118 118
 Use encrypt_string to create encrypted variables to embed in yaml
119 119
 `````````````````````````````````````````````````````````````````
120 120
 
121
-The :ref:`ansible-vault encrypt_string` command will encrypt and format a provided string into a format
121
+The :ref:`ansible-vault encrypt_string <ansible_vault_encrypt_string>` command will encrypt and format a provided string into a format
122 122
 that can be included in :ref:`ansible-playbook` YAML files.
123 123
 
124 124
 To encrypt a string provided as a cli arg:
125
+
125 126
 .. code-block:: bash
126 127
 
127 128
     ansible-vault encrypt_string --vault-id a_password_file 'foobar' --name 'the_secret'
... ...
@@ -224,7 +225,7 @@ Providing Vault Passwords
224 224
 `````````````````````````
225 225
 
226 226
 Since Ansible 2.4, the recommended way to provide a vault password from the cli is
227
-to use the :option:`--vault-id` cli option.
227
+to use the :option:`--vault-id <ansible-playbook --vault-id>` cli option.
228 228
 
229 229
 For example, to use a password store in the text file :file:`/path/to/my/vault-password-file`:
230 230
 
... ...
@@ -244,7 +245,7 @@ To get the password from a vault password executable script :file:`my-vault-pass
244 244
 
245 245
     ansible-playbook --vault-id my-vault-password.py
246 246
 
247
-The value for :option:`--vault-id` can specify the type of vault id (prompt, a file path, etc)
247
+The value for :option:`--vault-id <ansible-playbook --vault-id>` can specify the type of vault id (prompt, a file path, etc)
248 248
 and a label for the vault id ('dev', 'prod', 'cloud', etc)
249 249
 
250 250
 For example, to use a password file :file:`dev-password` for the vault-id 'dev':
... ...
@@ -261,20 +262,20 @@ To prompt for the 'dev' vault id:
261 261
 
262 262
 *Prior to Ansible 2.4*
263 263
 
264
-To be prompted for a vault password, use the :option:`--ask-vault-pass` cli option:
264
+To be prompted for a vault password, use the :option:`--ask-vault-pass <ansible-playbook --vault-id>` cli option:
265 265
 
266 266
 .. code-block:: bash
267 267
 
268 268
     ansible-playbook --ask-vault-pass site.yml
269 269
 
270
-To specify a vault password in a text file 'dev-password', use the :option:`--vault-password-file` option:
270
+To specify a vault password in a text file 'dev-password', use the :option:`--vault-password-file <ansible-playbook --vault-password-file>` option:
271 271
 
272 272
 .. code-block:: bash
273 273
 
274 274
     ansible-playbook --vault-password-file dev-password site.yml
275 275
 
276 276
 There is a config option (:ref:`DEFAULT_VAULT_PASSWORD_FILE`) to specify a vault password file to use
277
-without requiring the :option:`--vault-password-file` cli option.
277
+without requiring the :option:`--vault-password-file <ansible-playbook --vault-password-file>` cli option.
278 278
 
279 279
 via config
280 280
   :ref:`ANSIBLE_VAULT_PASSWORD_FILE`
... ...
@@ -287,7 +288,7 @@ via env
287 287
 Multiple vault passwords
288 288
 ^^^^^^^^^^^^^^^^^^^^^^^^
289 289
 
290
-Since Ansible 2.4 and later support using multiple vault passwords, :option:`--vault-id` can
290
+Since Ansible 2.4 and later support using multiple vault passwords, :option:`--vault-id <ansible-playbook --vault-id>` can
291 291
 be provided multiple times.
292 292
 
293 293
 If multiple vault passwords are provided, by default Ansible will attempt to decrypt vault content
... ...
@@ -302,7 +303,7 @@ For example, to use a 'dev' password read from a file and to be prompted for the
302 302
 In the above case, the 'dev' password will be tried first, then the 'prod' password for cases
303 303
 where Ansible doesn't know which vault id is used to encrypt something.
304 304
 
305
-If the vault content was encrypted using a :option:`--vault-id` option, then the label of the
305
+If the vault content was encrypted using a :option:`--vault-id <ansible-vault --vault-id>` option, then the label of the
306 306
 vault id is stored with the vault content. When Ansible knows the right vault-id, it will try
307 307
 the matching vault id's secret first before trying the rest of the vault-ids.
308 308
 
... ...
@@ -317,17 +318,17 @@ use. For example, instead of requiring the cli option on every use, the (:ref:`D
317 317
 
318 318
     ansible-playbook --vault-id dev@dev-password --vault-id prod@prompt site.yml
319 319
 
320
-The :option:`--vault-id` can be used in lieu of the :option:`--vault-password-file` or :option:`--ask-vault-pass` options,
320
+The :option:`--vault-id <ansible-playbook --vault-id>` can be used in lieu of the :option:`--vault-password-file <ansible-playbook --vault-password-file>` or :option:`--ask-vault-pass <ansible-playbook --ask-vault-pass>` options,
321 321
 or it can be used in combination with them.
322 322
 
323
-When using :ref:`ansible-vault` command that encrypt content (:ref:`ansible-vault encrypt`, :ref:`ansible-vault encrypt_string`, etc)
323
+When using :ref:`ansible-vault` commands that encrypt content (:ref:`ansible-vault encrypt <ansible_vault_encrypt>`, :ref:`ansible-vault encrypt_string <ansible_vault_encrypt_string>`, etc)
324 324
 only one vault-id can be used.
325 325
 
326 326
 
327 327
 
328 328
 .. note::
329 329
     Prior to Ansible 2.4, only one vault password could be used in each Ansible run. The
330
-    :option:`--vault-id` option is not support prior to Ansible 2.4.
330
+    :option:`--vault-id <ansible-playbook --vault-id>` option is not support prior to Ansible 2.4.
331 331
 
332 332
 
333 333
 .. _speeding_up_vault:
334 334
new file mode 100644
... ...
@@ -0,0 +1,139 @@
0
+{% set name = cli_name -%}
1
+{% set name_slug = cli_name -%}
2
+
3
+.. _{{name}}:
4
+
5
+{% set name_len = name|length + 0-%}
6
+{{ '=' * name_len }}
7
+{{name}}
8
+{{ '=' * name_len }}
9
+
10
+
11
+:strong:`{{short_desc|default('')}}`
12
+
13
+
14
+.. contents::
15
+   :local:
16
+   :depth: 2
17
+
18
+
19
+.. program:: {{cli_name}}
20
+
21
+Synopsis
22
+========
23
+
24
+.. code-block:: bash
25
+
26
+   {{ usage|replace('%prog', cli_name) }}
27
+
28
+
29
+Description
30
+===========
31
+
32
+
33
+{{ long_desc|default('', True)  }}
34
+
35
+{% if options %}
36
+Common Options
37
+==============
38
+
39
+
40
+{% for option in options|sort(attribute='options') %}
41
+
42
+.. option:: {% for switch in option['options'] %}{{switch}}{% if option['arg'] %} <{{option['arg']}}>{% endif %}{% if not loop.last %}, {% endif %}{% endfor %}
43
+
44
+   {{ option['desc'] }}
45
+{% endfor %}
46
+{% endif %}
47
+
48
+{% if arguments %}
49
+ARGUMENTS
50
+=========
51
+
52
+.. program:: {{cli_name}}
53
+
54
+{% for arg in arguments %}
55
+.. option:: {{ arg }}
56
+
57
+   {{ (arguments[arg]|default(' '))}}
58
+
59
+{% endfor %}
60
+{% endif %}
61
+
62
+{% if actions %}
63
+Actions
64
+=======
65
+
66
+{% for action in actions %}
67
+
68
+.. program:: {{cli_name}} {{action}}
69
+.. _{{cli_name|replace('-','_')}}_{{action}}:
70
+
71
+{{ action}}
72
+{{ '-' * action|length}}
73
+
74
+{{ (actions[action]['desc']|default(' '))}}
75
+
76
+{% if actions[action]['options'] %}
77
+
78
+
79
+{% for option in actions[action]['options']|sort %}
80
+.. option:: {% for switch in option['options'] if switch in actions[action]['option_names'] %}{{switch}} {% if option['arg'] %} <{{option['arg']}}>{% endif %}{% if not loop.last %}, {% endif %}{% endfor %}
81
+
82
+   {{ (option['desc']) }}
83
+{% endfor %}
84
+{% endif %}
85
+
86
+{% endfor %}
87
+.. program:: {{cli_name}}
88
+{% endif %}
89
+
90
+Environment
91
+===========
92
+
93
+The following environment variables may be specified.
94
+
95
+{% if inventory %}
96
+:envvar:`ANSIBLE_INVENTORY`  -- Override the default ansible inventory file
97
+
98
+{% endif %}
99
+{% if library %}
100
+:envvar:`ANSIBLE_LIBRARY` -- Override the default ansible module library path
101
+
102
+{% endif %}
103
+:envvar:`ANSIBLE_CONFIG` -- Override the default ansible config file
104
+
105
+Many more are available for most options in ansible.cfg
106
+
107
+
108
+Files
109
+=====
110
+
111
+{% if inventory %}
112
+:file:`/etc/ansible/hosts` -- Default inventory file
113
+
114
+{% endif %}
115
+:file:`/etc/ansible/ansible.cfg` -- Config file, used if present
116
+
117
+:file:`~/.ansible.cfg` -- User config file, overrides the default config if present
118
+
119
+Author
120
+======
121
+
122
+Ansible was originally written by Michael DeHaan.
123
+
124
+See the `AUTHORS` file for a complete list of contributors.
125
+
126
+
127
+Copyright
128
+=========
129
+
130
+Copyright © 2017 Red Hat, Inc | Ansible.
131
+
132
+Ansible is released under the terms of the GPLv3 License.
133
+
134
+See also
135
+========
136
+
137
+{% for other in cli_bin_name_list|sort %}:manpage:`{{other}}(1)`,  {% endfor %}
138
+
... ...
@@ -20,8 +20,17 @@ SYNOPSIS
20 20
 
21 21
 DESCRIPTION
22 22
 -----------
23
-*{{name}}* {{ long_desc|default('', True)|wordwrap }}
23
+{{ long_desc|default('', True)|wordwrap }}
24 24
 
25
+{% if options %}
26
+COMMON OPTIONS
27
+--------------
28
+{% for option in options|sort(attribute='options') %}
29
+{% for switch in option['options'] %}*{{switch}}*{% if option['arg'] %} '{{option['arg']}}'{% endif %}{% if not loop.last %}, {% endif %}{% endfor %}::
30
+
31
+{{ option['desc'] }}
32
+{% endfor %}
33
+{% endif %}
25 34
 
26 35
 {% if arguments %}
27 36
 ARGUMENTS
... ...
@@ -38,22 +47,19 @@ ARGUMENTS
38 38
 {% if actions %}
39 39
 ACTIONS
40 40
 -------
41
-
42 41
 {% for action in actions %}
43
-{{ action }}
42
+    *{{ action }}*::: {{ (actions[action]['desc']|default(' '))|wordwrap}}
44 43
 
45
-{{ (actions[action]|default(' '))|wordwrap}}
44
+{% if actions[action]['options'] %}
45
+{% for option in actions[action]['options']|sort %}
46
+{% for switch in option['options'] if switch in actions[action]['option_names'] %}*{{switch}}*{% if option['arg'] %} '{{option['arg']}}'{% endif %}{% if not loop.last %}, {% endif %}{% endfor %}::
46 47
 
48
+        {{ (option['desc']) }}
47 49
 {% endfor %}
48 50
 {% endif %}
49
-OPTIONS
50
-
51
-{% for option in options|sort(attribute='options') %}
52
-{% for switch in option['options'] %}*{{switch}}* {% if option['arg'] %}'{{option['arg']}}'{% endif %}{% if not loop.last %}, {% endif %}{% endfor %}::
53
-
54
-{{ option['desc'] }}
55 51
 {% endfor %}
52
+{% endif %}
53
+
56 54
 
57 55
 {% if inventory %}
58 56
 INVENTORY
... ...
@@ -111,7 +117,7 @@ Ansible is released under the terms of the GPLv3 License.
111 111
 SEE ALSO
112 112
 --------
113 113
 
114
- {% for other in cli_list|sort %}{% if other != cli %}*ansible{% if other != 'adhoc' %}-{{other}}{% endif %}*(1){% if not loop.last %}, {% endif %}{% endif %}{% endfor %}
114
+{% for other in cli_list|sort %}{% if other != cli %}*ansible{% if other != 'adhoc' %}-{{other}}{% endif %}*(1){% if not loop.last %}, {% endif %}{% endif %}{% endfor %}
115 115
 
116 116
 Extensive documentation is available in the documentation site:
117 117
 <http://docs.ansible.com>.
... ...
@@ -56,7 +56,8 @@ class ConfigCLI(CLI):
56 56
 
57 57
         self.parser = CLI.base_parser(
58 58
             usage = "usage: %%prog [%s] [--help] [options] [ansible.cfg]" % "|".join(self.VALID_ACTIONS),
59
-            epilog = "\nSee '%s <command> --help' for more information on a specific command.\n\n" % os.path.basename(sys.argv[0])
59
+            epilog = "\nSee '%s <command> --help' for more information on a specific command.\n\n" % os.path.basename(sys.argv[0]),
60
+            desc="View, edit, and manage ansible configuration.",
60 61
         )
61 62
 
62 63
         self.parser.add_option('-c', '--config', dest='config_file', help="path to configuration file, defaults to first file found in precedence.")