648e4698ed
* re-use ``make_definition`` function for displaying parameters of plugins. Profit: unified view; references to each argument of each plugin. * Use list in ``make_definition`` function * fix indention of plugins parameters descriptions Change-Id: I0b7e567134d36abde313d5538eab99b282f60178
164 lines
6.1 KiB
Python
164 lines
6.1 KiB
Python
# Copyright 2015: 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.
|
|
|
|
from docutils.parsers import rst
|
|
import re
|
|
|
|
from rally.common.plugin import discover
|
|
from rally.common.plugin import plugin
|
|
from rally import plugins
|
|
from utils import category, subcategory, section, paragraph, parse_text, \
|
|
make_definition
|
|
|
|
|
|
CATEGORIES = {
|
|
"Common": ["OS Client"],
|
|
"Deployment": ["Engine", "Provider Factory"],
|
|
"Task Component": ["Chart", "Context", "Exporter", "Hook",
|
|
"Resource Type", "SLA", "Scenario", "Scenario Runner",
|
|
"Trigger"],
|
|
"Verification Component": ["Verifier Context", "Verification Reporter",
|
|
"Verifier Manager"]
|
|
}
|
|
# NOTE(andreykurilin): several bases do not have docstings at all, so it is
|
|
# redundant to display them
|
|
IGNORED_BASES = ["Resource Type", "Task Exporter", "OS Client"]
|
|
|
|
|
|
class PluginsReferenceDirective(rst.Directive):
|
|
optional_arguments = 1
|
|
option_spec = {"base_cls": str}
|
|
|
|
@staticmethod
|
|
def _make_pretty_parameters(parameters, ref_prefix):
|
|
if not parameters:
|
|
return []
|
|
|
|
results = [paragraph("**Parameters**:")]
|
|
for p in parameters:
|
|
pname = p["name"]
|
|
ref = ("%s%s" % (ref_prefix, pname)).lower().replace(".", "-")
|
|
if "type" in p:
|
|
pname += " (%s)" % p["type"]
|
|
pdoc = "\n ".join(p["doc"].split("\n"))
|
|
results.extend(make_definition(pname, ref, [pdoc]))
|
|
return results
|
|
|
|
def _make_plugin_section(self, plugin_cls, base_name=None):
|
|
section_name = plugin_cls.get_name()
|
|
if base_name:
|
|
section_name += " [%s]" % base_name
|
|
section_obj = section(section_name)
|
|
|
|
info = plugin_cls.get_info()
|
|
if info["title"]:
|
|
section_obj.append(paragraph(info["title"]))
|
|
|
|
if info["description"]:
|
|
section_obj.extend(parse_text(info["description"]))
|
|
|
|
if info["namespace"]:
|
|
section_obj.append(paragraph(
|
|
"**Namespace**: %s" % info["namespace"]))
|
|
|
|
if info["parameters"]:
|
|
if base_name:
|
|
ref_prefix = "%s-%s-" % (base_name, plugin_cls.get_name())
|
|
else:
|
|
ref_prefix = "%s-" % plugin_cls.get_name()
|
|
|
|
section_obj.extend(self._make_pretty_parameters(info["parameters"],
|
|
ref_prefix))
|
|
|
|
if info["returns"]:
|
|
section_obj.extend(parse_text(
|
|
"**Returns**:\n%s" % info["returns"]))
|
|
|
|
filename = info["module"].replace(".", "/")
|
|
ref = "https://github.com/openstack/rally/blob/master/%s.py" % filename
|
|
section_obj.extend(parse_text("**Module**:\n`%s`__\n\n__ %s"
|
|
% (info["module"], ref)))
|
|
return section_obj
|
|
|
|
def _make_plugin_base_section(self, base_cls, base_name=None):
|
|
if base_name:
|
|
title = ("%ss" % base_name if base_name[-1] != "y"
|
|
else "%sies" % base_name[:-1])
|
|
subcategory_obj = subcategory(title)
|
|
else:
|
|
subcategory_obj = []
|
|
for p in sorted(base_cls.get_all(), key=lambda o: o.get_name()):
|
|
subcategory_obj.append(self._make_plugin_section(p, base_name))
|
|
|
|
return subcategory_obj
|
|
|
|
@staticmethod
|
|
def _parse_class_name(cls):
|
|
name = ""
|
|
for word in re.split(r'([A-Z][a-z]*)', cls.__name__):
|
|
if word:
|
|
if len(word) > 1 and name:
|
|
name += " "
|
|
name += word
|
|
return name
|
|
|
|
def _get_all_plugins_bases(self):
|
|
"""Return grouped and sorted all plugins bases."""
|
|
bases = []
|
|
bases_names = []
|
|
for p in discover.itersubclasses(plugin.Plugin):
|
|
base_ref = getattr(p, "base_ref", None)
|
|
if base_ref == p:
|
|
name = self._parse_class_name(p)
|
|
if name in bases_names:
|
|
raise Exception("Two base classes with same name '%s' are "
|
|
"detected." % name)
|
|
bases_names.append(name)
|
|
category_of_base = "Common"
|
|
for cname, cbases in CATEGORIES.items():
|
|
if name in cbases:
|
|
category_of_base = cname
|
|
|
|
bases.append((category_of_base, name, p))
|
|
return sorted(bases)
|
|
|
|
def run(self):
|
|
plugins.load()
|
|
bases = self._get_all_plugins_bases()
|
|
if "base_cls" in self.options:
|
|
for _category_name, base_name, base_cls in bases:
|
|
if base_name == self.options["base_cls"]:
|
|
return self._make_plugin_base_section(base_cls)
|
|
raise Exception("Failed to generate plugins reference for '%s'"
|
|
" plugin base." % self.options["base_cls"])
|
|
|
|
categories = {}
|
|
|
|
for category_name, base_name, base_cls in bases:
|
|
# FIXME(andreykurilin): do not ignore anything
|
|
if base_name in IGNORED_BASES:
|
|
continue
|
|
if category_name not in categories:
|
|
categories[category_name] = category(category_name)
|
|
category_of_base = categories[category_name]
|
|
category_of_base.append(self._make_plugin_base_section(base_cls,
|
|
base_name))
|
|
return [content for _name, content in sorted(categories.items())]
|
|
|
|
|
|
def setup(app):
|
|
plugins.load()
|
|
app.add_directive("generate_plugin_reference", PluginsReferenceDirective)
|