[Docs] Add cli reference to docs
Change-Id: Ie6497abc115c81753b0d51f87aeeac664d6720cf
This commit is contained in:
parent
2481d205f9
commit
21157c92a8
@ -22,6 +22,8 @@ RUN ./install_rally.sh --system --verbose --yes \
|
|||||||
--db-name /home/rally/.rally.sqlite && \
|
--db-name /home/rally/.rally.sqlite && \
|
||||||
pip install -r optional-requirements.txt && \
|
pip install -r optional-requirements.txt && \
|
||||||
mkdir /opt/rally/ && \
|
mkdir /opt/rally/ && \
|
||||||
|
# TODO(andreykurilin): build docs to rst before move, since we have several
|
||||||
|
# extensions.
|
||||||
mv certification/ samples/ doc/ /opt/rally/ && \
|
mv certification/ samples/ doc/ /opt/rally/ && \
|
||||||
chmod -R u=rwX,go=rX /opt/rally /etc/rally && \
|
chmod -R u=rwX,go=rX /opt/rally /etc/rally && \
|
||||||
rm -rf /tmp/* && \
|
rm -rf /tmp/* && \
|
||||||
|
206
doc/ext/cli_reference.py
Normal file
206
doc/ext/cli_reference.py
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
# Copyright 2016: Mirantis 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 copy
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
from docutils.parsers import rst
|
||||||
|
|
||||||
|
from rally.cli import cliutils
|
||||||
|
from rally.cli import main
|
||||||
|
from rally.cli import manage
|
||||||
|
from utils import (category, subcategory, hint, make_definition, note,
|
||||||
|
paragraph, parse_text, warning)
|
||||||
|
|
||||||
|
|
||||||
|
class Parser(object):
|
||||||
|
"""A simplified interface of argparse.ArgumentParser"""
|
||||||
|
def __init__(self):
|
||||||
|
self.parsers = {}
|
||||||
|
self.subparser = None
|
||||||
|
self.defaults = {}
|
||||||
|
self.arguments = []
|
||||||
|
|
||||||
|
def add_parser(self, name, help=None, description=None,
|
||||||
|
formatter_class=None):
|
||||||
|
parser = Parser()
|
||||||
|
self.parsers[name] = {"description": description,
|
||||||
|
"help": help,
|
||||||
|
"fclass": formatter_class,
|
||||||
|
"parser": parser}
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def set_defaults(self, command_object=None, action_fn=None,
|
||||||
|
action_kwargs=None):
|
||||||
|
if command_object:
|
||||||
|
self.defaults["command_object"] = command_object
|
||||||
|
if action_fn:
|
||||||
|
self.defaults["action_fn"] = action_fn
|
||||||
|
if action_kwargs:
|
||||||
|
self.defaults["action_kwargs"] = action_kwargs
|
||||||
|
|
||||||
|
def add_subparsers(self, dest):
|
||||||
|
# NOTE(andreykurilin): there is only one expected call
|
||||||
|
if self.subparser:
|
||||||
|
raise ValueError("Can't add one more subparser.")
|
||||||
|
self.subparser = Parser()
|
||||||
|
return self.subparser
|
||||||
|
|
||||||
|
def add_argument(self, *args, **kwargs):
|
||||||
|
if "action_args" in args:
|
||||||
|
return
|
||||||
|
self.arguments.append((args, kwargs))
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_UUIDS_CMD = {
|
||||||
|
"deployment": ["rally deployment create"],
|
||||||
|
"task": ["rally task start"],
|
||||||
|
"verification": ["rally verify start", "rally verify import_results"]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def compose_note_about_default_uuids(argument, dest):
|
||||||
|
# TODO(andreykurilin): add references to commands
|
||||||
|
return note("The default value for the ``%(arg)s`` argument is taken from "
|
||||||
|
"the Rally environment. Usually, the default value is equal to"
|
||||||
|
" the UUID of the last successful run of ``%(cmd)s``, if the "
|
||||||
|
"``--no-use`` argument was not used." % {
|
||||||
|
"arg": argument,
|
||||||
|
"cmd": "``, ``".join(DEFAULT_UUIDS_CMD[dest])})
|
||||||
|
|
||||||
|
|
||||||
|
def compose_use_cmd_hint_msg(cmd):
|
||||||
|
return hint("You can set the default value by executing ``%(cmd)s <uuid>``"
|
||||||
|
" (ref__).\n\n __ #%(ref)s" % {"cmd": cmd,
|
||||||
|
"ref": cmd.replace(" ", "-")})
|
||||||
|
|
||||||
|
|
||||||
|
def make_arguments_section(category_name, cmd_name, arguments, defaults):
|
||||||
|
elements = [paragraph("**Command arguments**:")]
|
||||||
|
for args, kwargs in arguments:
|
||||||
|
# for future changes...
|
||||||
|
# :param args: a single command argument which can represented by
|
||||||
|
# several names(for example, --uuid and --task-id) in cli.
|
||||||
|
# :type args: tuple
|
||||||
|
# :param kwargs: description of argument. Have next format:
|
||||||
|
# {"dest": "action_kwarg_<name of keyword argument in code>",
|
||||||
|
# "help": "just a description of argument"
|
||||||
|
# "metavar": "[optional] metavar of argument. Example:"
|
||||||
|
# "Example: argument '--file'; metavar 'path' ",
|
||||||
|
# "type": "[optional] class object of argument's type",
|
||||||
|
# "required": "[optional] boolean value"}
|
||||||
|
# :type kwargs: dict
|
||||||
|
dest = kwargs.get("dest").replace("action_kwarg_", "")
|
||||||
|
description = []
|
||||||
|
if cmd_name != "use":
|
||||||
|
# lets add notes about specific default values and hint about
|
||||||
|
# "use" command with reference
|
||||||
|
if dest in ("deployment", "task"):
|
||||||
|
description.append(compose_note_about_default_uuids(
|
||||||
|
args[0], dest))
|
||||||
|
description.append(
|
||||||
|
compose_use_cmd_hint_msg("rally %s use" % dest))
|
||||||
|
elif dest == "verification":
|
||||||
|
description.append(compose_note_about_default_uuids(
|
||||||
|
args[0], dest))
|
||||||
|
description.append(
|
||||||
|
compose_use_cmd_hint_msg("rally %verify use"))
|
||||||
|
|
||||||
|
description.append(kwargs.get("help"))
|
||||||
|
|
||||||
|
action = kwargs.get("action")
|
||||||
|
if not action:
|
||||||
|
arg_type = kwargs.get("type")
|
||||||
|
if arg_type:
|
||||||
|
description.append("**Type**: %s" % arg_type.__name__)
|
||||||
|
|
||||||
|
skip_default = dest in ("deployment",
|
||||||
|
"task_id",
|
||||||
|
"verification")
|
||||||
|
if not skip_default and dest in defaults:
|
||||||
|
description.append("**Default**: %s" % defaults[dest])
|
||||||
|
metavar = kwargs.get("metavar")
|
||||||
|
|
||||||
|
ref = "%s_%s_%s" % (category_name, cmd_name, args[0].replace("-", ""))
|
||||||
|
|
||||||
|
if metavar:
|
||||||
|
args = ["%s %s" % (arg, metavar) for arg in args]
|
||||||
|
|
||||||
|
elements.extend(make_definition(", ".join(args), ref, description))
|
||||||
|
return elements
|
||||||
|
|
||||||
|
|
||||||
|
def get_defaults(func):
|
||||||
|
"""Return a map of argument:default_value for specified function."""
|
||||||
|
spec = inspect.getargspec(func)
|
||||||
|
if spec.defaults:
|
||||||
|
return dict(zip(spec.args[-len(spec.defaults):], spec.defaults))
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def make_command_section(category_name, name, parser):
|
||||||
|
# NOTE(andreykurilin): there is only one category in rally-manage, so
|
||||||
|
# let's just hardcode it.
|
||||||
|
cmd = "rally-manage" if category_name == "db" else "rally"
|
||||||
|
section = subcategory("%s %s %s" % (cmd, category_name, name))
|
||||||
|
section.extend(parse_text(parser["description"]))
|
||||||
|
if parser["parser"].arguments:
|
||||||
|
defaults = get_defaults(parser["parser"].defaults["action_fn"])
|
||||||
|
section.extend(make_arguments_section(
|
||||||
|
category_name, name, parser["parser"].arguments, defaults))
|
||||||
|
return section
|
||||||
|
|
||||||
|
|
||||||
|
def make_category_section(name, parser):
|
||||||
|
category_obj = category("Category: %s" % name)
|
||||||
|
# NOTE(andreykurilin): we are re-using `_add_command_parsers` method from
|
||||||
|
# `rally.cli.cliutils`, but, since it was designed to print help message,
|
||||||
|
# generated description for categories contains specification for all
|
||||||
|
# sub-commands. We don't need information about sub-commands at this point,
|
||||||
|
# so let's skip "generated description" and take it directly from category
|
||||||
|
# class.
|
||||||
|
description = parser.defaults["command_object"].__doc__
|
||||||
|
# TODO(andreykurilin): write a decorator which will mark cli-class as
|
||||||
|
# deprecated without changing its docstring.
|
||||||
|
if description.startswith("[Deprecated"):
|
||||||
|
i = description.find("]")
|
||||||
|
msg = description[1:i]
|
||||||
|
description = description[i+1:].strip()
|
||||||
|
category_obj.append(warning(msg))
|
||||||
|
category_obj.extend(parse_text(description))
|
||||||
|
|
||||||
|
for command in sorted(parser.subparser.parsers.keys()):
|
||||||
|
subparser = parser.subparser.parsers[command]
|
||||||
|
category_obj.append(make_command_section(name, command, subparser))
|
||||||
|
return category_obj
|
||||||
|
|
||||||
|
|
||||||
|
class CLIReferenceDirective(rst.Directive):
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
parser = Parser()
|
||||||
|
categories = copy.copy(main.categories)
|
||||||
|
categories["db"] = manage.DBCommands
|
||||||
|
cliutils._add_command_parsers(categories, parser)
|
||||||
|
|
||||||
|
content = []
|
||||||
|
for category in sorted(categories.keys()):
|
||||||
|
content.append(make_category_section(
|
||||||
|
category, parser.parsers[category]["parser"]))
|
||||||
|
return content
|
||||||
|
|
||||||
|
|
||||||
|
def setup(app):
|
||||||
|
app.add_directive("make_cli_reference", CLIReferenceDirective)
|
@ -122,4 +122,4 @@ class PluginReferenceDirective(rst.Directive):
|
|||||||
|
|
||||||
def setup(app):
|
def setup(app):
|
||||||
plugins.load()
|
plugins.load()
|
||||||
app.add_directive('generate_plugin_reference', PluginReferenceDirective)
|
app.add_directive("generate_plugin_reference", PluginReferenceDirective)
|
||||||
|
@ -43,7 +43,7 @@ def make_definition(term, ref, descriptions):
|
|||||||
"""Constructs definition with reference to it"""
|
"""Constructs definition with reference to it"""
|
||||||
ref = ref.replace("_", "-").replace(" ", "-")
|
ref = ref.replace("_", "-").replace(" ", "-")
|
||||||
definition = parse_text(
|
definition = parse_text(
|
||||||
".. _%(ref)s:\n\n%(term)s (ref__)\n\n__ #%(ref)s" %
|
".. _%(ref)s:\n\n*%(term)s* (ref__)\n\n__ #%(ref)s" %
|
||||||
{"ref": ref, "term": term})
|
{"ref": ref, "term": term})
|
||||||
for descr in descriptions:
|
for descr in descriptions:
|
||||||
if isinstance(descr, (six.text_type, six.binary_type)):
|
if isinstance(descr, (six.text_type, six.binary_type)):
|
||||||
|
7
doc/source/cli/cli_reference.rst
Normal file
7
doc/source/cli/cli_reference.rst
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.. _cli_reference:
|
||||||
|
|
||||||
|
|
||||||
|
Command Line Interface
|
||||||
|
======================
|
||||||
|
|
||||||
|
.. make_cli_reference::
|
@ -47,6 +47,7 @@ extensions = [
|
|||||||
"sphinx.ext.coverage",
|
"sphinx.ext.coverage",
|
||||||
"sphinx.ext.ifconfig",
|
"sphinx.ext.ifconfig",
|
||||||
"sphinx.ext.viewcode",
|
"sphinx.ext.viewcode",
|
||||||
|
"ext.cli_reference",
|
||||||
"ext.plugin_reference"
|
"ext.plugin_reference"
|
||||||
]
|
]
|
||||||
todo_include_todos = True
|
todo_include_todos = True
|
||||||
@ -161,7 +162,7 @@ html_last_updated_fmt = subprocess.Popen(git_cmd,
|
|||||||
|
|
||||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||||
# typographically correct entities.
|
# typographically correct entities.
|
||||||
# html_use_smartypants = True
|
html_use_smartypants = False
|
||||||
|
|
||||||
# Custom sidebar templates, maps document names to template names.
|
# Custom sidebar templates, maps document names to template names.
|
||||||
# html_sidebars = {}
|
# html_sidebars = {}
|
||||||
|
@ -30,6 +30,7 @@ Contents
|
|||||||
overview
|
overview
|
||||||
install
|
install
|
||||||
tutorial
|
tutorial
|
||||||
|
cli/cli_reference
|
||||||
user_stories
|
user_stories
|
||||||
plugins
|
plugins
|
||||||
plugin/plugin_reference
|
plugin/plugin_reference
|
||||||
|
@ -701,7 +701,7 @@ class TaskCommands(object):
|
|||||||
|
|
||||||
@cliutils.args("--force", action="store_true", help="force delete")
|
@cliutils.args("--force", action="store_true", help="force delete")
|
||||||
@cliutils.args("--uuid", type=str, dest="task_id", nargs="*",
|
@cliutils.args("--uuid", type=str, dest="task_id", nargs="*",
|
||||||
metavar="TASK_ID",
|
metavar="<task-id>",
|
||||||
help="UUID of task or a list of task UUIDs.")
|
help="UUID of task or a list of task UUIDs.")
|
||||||
@envutils.with_default_task_id
|
@envutils.with_default_task_id
|
||||||
def delete(self, task_id=None, force=False):
|
def delete(self, task_id=None, force=False):
|
||||||
|
Loading…
Reference in New Issue
Block a user