68fc28527a
Result of running $ pyupgrade --py38-plus $(git ls-files | grep ".py$") This was inspired by Nova [1] and Octavia [2] Fixed PEP8 errors introduced by pyupgrade by running: $ autopep8 --select=E127,E128,E501 --max-line-length 79 -r \ --in-place designate and manual updates. [1]: https://review.opendev.org/c/openstack/nova/+/896986 [2]: https://review.opendev.org/c/openstack/octavia/+/899263 Change-Id: Idfa757d7ba238012db116fdb3e98cc7c5ff4b169
451 lines
15 KiB
Python
451 lines
15 KiB
Python
# Copyright (C) 2014 Red Hat, Inc.
|
|
#
|
|
# 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.
|
|
|
|
"""
|
|
This provides a sphinx extension able to render the source/support-matrix.ini
|
|
file into the developer documentation.
|
|
|
|
It is used via a single directive in the .rst file
|
|
|
|
.. support_matrix::
|
|
|
|
"""
|
|
import configparser as config_parser
|
|
import os
|
|
|
|
from docutils import nodes
|
|
from docutils.parsers import rst
|
|
from sphinx.util import logging
|
|
from sphinx.util.osutil import copyfile
|
|
|
|
from designate.backend.base import Backend
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class SupportMatrix:
|
|
"""Represents the entire support matrix for Nova virt drivers
|
|
"""
|
|
|
|
def __init__(self):
|
|
# List of SupportMatrixFeature instances, describing
|
|
# all the features present in Nova virt drivers
|
|
self.grades = []
|
|
|
|
self.grade_names = {}
|
|
self.grade_classes = {}
|
|
|
|
# Dict of (name, SupportMatrixTarget) enumerating
|
|
# all the hypervisor drivers that have data recorded
|
|
# for them in self.features. The 'name' dict key is
|
|
# the value from the SupportMatrixTarget.key attribute
|
|
self.backends = {}
|
|
|
|
|
|
class SupportMatrixGrade:
|
|
|
|
def __init__(self, key, title, notes, in_tree, css_class):
|
|
self.key = key
|
|
self.title = title
|
|
self.notes = notes
|
|
self.in_tree = in_tree
|
|
self.css_class = css_class
|
|
|
|
|
|
class SupportMatrixBackend:
|
|
|
|
def __init__(self, key, title, status,
|
|
maintainers=None, variations=None,
|
|
repository=None, type=None):
|
|
self.key = key
|
|
self.title = title
|
|
self.type = type
|
|
self.status = status
|
|
self.maintainers = maintainers
|
|
self.variations = variations
|
|
self.repository = repository
|
|
|
|
|
|
class SupportMatrixDirective(rst.Directive):
|
|
|
|
option_spec = {
|
|
'support-matrix': str,
|
|
}
|
|
|
|
def run(self):
|
|
matrix = self._load_support_matrix()
|
|
return self._build_markup(matrix)
|
|
|
|
def _load_support_matrix(self):
|
|
"""Reads the support-matrix.ini file and populates an instance
|
|
of the SupportMatrix class with all the data.
|
|
|
|
:returns: SupportMatrix instance
|
|
"""
|
|
|
|
cfg = config_parser.ConfigParser()
|
|
env = self.state.document.settings.env
|
|
fname = self.options.get("support-matrix",
|
|
"support-matrix.ini")
|
|
rel_fpath, fpath = env.relfn2path(fname)
|
|
with open(fpath) as fp:
|
|
cfg.read_file(fp)
|
|
|
|
# This ensures that the docs are rebuilt whenever the
|
|
# .ini file changes
|
|
env.note_dependency(rel_fpath)
|
|
|
|
matrix = SupportMatrix()
|
|
|
|
# The 'targets' section is special - it lists all the
|
|
# hypervisors that this file records data for
|
|
for item in cfg.options("backends"):
|
|
if not item.startswith("backend-impl-"):
|
|
continue
|
|
|
|
# The driver string will optionally contain
|
|
# a hypervisor and architecture qualifier
|
|
# so we expect between 1 and 3 components
|
|
# in the name
|
|
key = item[13:]
|
|
title = cfg.get("backends", item)
|
|
name = key.split("-")
|
|
|
|
try:
|
|
status = cfg.get("backends.%s" % item, "status")
|
|
except config_parser.NoOptionError:
|
|
if cfg.get("backends.%s" % item, "type") == "xfr":
|
|
backend = Backend.get_driver(name[0])
|
|
status = backend.__backend_status__
|
|
|
|
if len(name) == 1:
|
|
backend = SupportMatrixBackend(
|
|
key, title, status, name[0])
|
|
elif len(name) == 2:
|
|
backend = SupportMatrixBackend(
|
|
key, title, status, name[0], variations=name[1])
|
|
else:
|
|
raise Exception("'%s' field is malformed in '[%s]' section" %
|
|
(item, "DEFAULT"))
|
|
|
|
backend.in_tree = cfg.getboolean(
|
|
"backends.%s" % item, "in-tree")
|
|
backend.type = cfg.get(
|
|
"backends.%s" % item, "type")
|
|
backend.notes = cfg.get(
|
|
"backends.%s" % item, "notes")
|
|
backend.repository = cfg.get(
|
|
"backends.%s" % item, "repository")
|
|
backend.maintainers = cfg.get(
|
|
"backends.%s" % item, "maintainers")
|
|
|
|
matrix.backends[key] = backend
|
|
|
|
grades = cfg.get("grades", "valid-grades")
|
|
|
|
grades = grades.split(",")
|
|
|
|
for grade in grades:
|
|
title = cfg.get("grades.%s" % grade, "title")
|
|
notes = cfg.get("grades.%s" % grade, "notes")
|
|
in_tree = cfg.get("grades.%s" % grade, "in-tree")
|
|
css_class = cfg.get("grades.%s" % grade, "css-class")
|
|
|
|
matrix.grade_names[grade] = title
|
|
matrix.grade_classes[grade] = css_class
|
|
|
|
grade = SupportMatrixGrade(
|
|
grade, title, notes, in_tree, css_class)
|
|
|
|
matrix.grades.append(grade)
|
|
|
|
return matrix
|
|
|
|
def _build_markup(self, matrix):
|
|
"""Constructs the docutils content for the support matrix
|
|
"""
|
|
content = []
|
|
self._build_grade_listing(matrix, content)
|
|
self._build_grade_table(matrix, content)
|
|
self._build_backend_detail(matrix, content)
|
|
return content
|
|
|
|
def _build_backend_detail_table(self, backend, matrix):
|
|
|
|
table = nodes.table()
|
|
table['classes'].append("table")
|
|
table['classes'].append("table-condensed")
|
|
tgroup = nodes.tgroup(cols=2)
|
|
tbody = nodes.tbody()
|
|
|
|
for i in range(2):
|
|
tgroup.append(nodes.colspec(colwidth=1))
|
|
|
|
tgroup.append(tbody)
|
|
table.append(tgroup)
|
|
|
|
graderow = nodes.row()
|
|
gradetitle = nodes.entry()
|
|
gradetitle.append(nodes.strong(text="Grade"))
|
|
gradetext = nodes.entry()
|
|
class_name = "label-%s" % matrix.grade_classes[backend.status]
|
|
status_text = nodes.paragraph(
|
|
text=matrix.grade_names[backend.status])
|
|
status_text['classes'].append(class_name)
|
|
status_text['classes'].append("label")
|
|
gradetext.append(status_text)
|
|
graderow.append(gradetitle)
|
|
graderow.append(gradetext)
|
|
tbody.append(graderow)
|
|
|
|
treerow = nodes.row()
|
|
treetitle = nodes.entry()
|
|
treetitle.append(nodes.strong(text="In Tree"))
|
|
if bool(backend.in_tree):
|
|
status = "\u2714"
|
|
intree = nodes.paragraph(text=status)
|
|
intree['classes'].append("label")
|
|
intree['classes'].append("label-success")
|
|
|
|
else:
|
|
status = "\u2716"
|
|
intree = nodes.paragraph(text=status)
|
|
intree['classes'].append("label")
|
|
intree['classes'].append("label-danger")
|
|
status = "\u2714"
|
|
treetext = nodes.entry()
|
|
treetext.append(intree)
|
|
treerow.append(treetitle)
|
|
treerow.append(treetext)
|
|
tbody.append(treerow)
|
|
|
|
maintrow = nodes.row()
|
|
mainttitle = nodes.entry()
|
|
mainttitle.append(nodes.strong(text="Maintainers"))
|
|
mainttext = nodes.entry()
|
|
mainttext.append(nodes.paragraph(text=backend.maintainers))
|
|
maintrow.append(mainttitle)
|
|
maintrow.append(mainttext)
|
|
tbody.append(maintrow)
|
|
|
|
reporow = nodes.row()
|
|
repotitle = nodes.entry()
|
|
repotitle.append(nodes.strong(text="Repository"))
|
|
repotext = nodes.entry()
|
|
repotext.append(nodes.paragraph(text=backend.repository))
|
|
reporow.append(repotitle)
|
|
reporow.append(repotext)
|
|
tbody.append(reporow)
|
|
|
|
noterow = nodes.row()
|
|
notetitle = nodes.entry()
|
|
notetitle.append(nodes.strong(text="Notes"))
|
|
notetext = nodes.entry()
|
|
notetext.append(nodes.paragraph(text=backend.notes))
|
|
noterow.append(notetitle)
|
|
noterow.append(notetext)
|
|
tbody.append(noterow)
|
|
|
|
return table
|
|
|
|
def _build_backend_detail(self, matrix, content):
|
|
|
|
detailstitle = nodes.subtitle(text="Backend Details")
|
|
content.append(detailstitle)
|
|
content.append(nodes.paragraph())
|
|
|
|
for key in matrix.backends.keys():
|
|
content.append(
|
|
nodes.subtitle(text=matrix.backends[key].title))
|
|
content.append(
|
|
self._build_backend_detail_table(
|
|
matrix.backends[key],
|
|
matrix))
|
|
|
|
content.append(nodes.paragraph())
|
|
|
|
return content
|
|
|
|
def _build_grade_listing(self, matrix, content):
|
|
|
|
summarytitle = nodes.subtitle(text="Grades")
|
|
content.append(nodes.raw(text="Grades", attributes={'tagname': 'h2'}))
|
|
content.append(summarytitle)
|
|
table = nodes.table()
|
|
table['classes'].append("table")
|
|
table['classes'].append("table-condensed")
|
|
grades = matrix.grades
|
|
|
|
tablegroup = nodes.tgroup(cols=2)
|
|
summarybody = nodes.tbody()
|
|
summaryhead = nodes.thead()
|
|
|
|
for i in range(2):
|
|
tablegroup.append(nodes.colspec(colwidth=1))
|
|
tablegroup.append(summaryhead)
|
|
tablegroup.append(summarybody)
|
|
table.append(tablegroup)
|
|
content.append(table)
|
|
|
|
header = nodes.row()
|
|
blank = nodes.entry()
|
|
blank.append(nodes.strong(text="Grade"))
|
|
header.append(blank)
|
|
|
|
blank = nodes.entry()
|
|
blank.append(nodes.strong(text="Description"))
|
|
header.append(blank)
|
|
|
|
summaryhead.append(header)
|
|
|
|
for grade in grades:
|
|
item = nodes.row()
|
|
namecol = nodes.entry()
|
|
class_name = "label-%s" % grade.css_class
|
|
status_text = nodes.paragraph(text=grade.title)
|
|
status_text['classes'].append(class_name)
|
|
status_text['classes'].append("label")
|
|
namecol.append(status_text)
|
|
item.append(namecol)
|
|
|
|
notescol = nodes.entry()
|
|
notescol.append(nodes.paragraph(text=grade.notes))
|
|
item.append(notescol)
|
|
|
|
summarybody.append(item)
|
|
|
|
return content
|
|
|
|
def _build_grade_table(self, matrix, content):
|
|
|
|
summarytitle = nodes.subtitle(text="Backends - Summary")
|
|
summary = nodes.table()
|
|
summary['classes'].append("table")
|
|
summary['classes'].append("table-condensed")
|
|
summarygroup = nodes.tgroup(cols=5)
|
|
summarybody = nodes.tbody()
|
|
summaryhead = nodes.thead()
|
|
|
|
for i in range(5):
|
|
summarygroup.append(nodes.colspec(colwidth=1))
|
|
summarygroup.append(summaryhead)
|
|
summarygroup.append(summarybody)
|
|
summary.append(summarygroup)
|
|
content.append(summarytitle)
|
|
content.append(summary)
|
|
|
|
header = nodes.row()
|
|
blank = nodes.entry()
|
|
blank.append(nodes.strong(text="Backend"))
|
|
header.append(blank)
|
|
|
|
blank = nodes.entry()
|
|
blank.append(nodes.strong(text="Status"))
|
|
header.append(blank)
|
|
|
|
blank = nodes.entry()
|
|
blank.append(nodes.strong(text="Type"))
|
|
header.append(blank)
|
|
|
|
blank = nodes.entry()
|
|
blank.append(nodes.strong(text="In Tree"))
|
|
header.append(blank)
|
|
|
|
blank = nodes.entry()
|
|
blank.append(nodes.strong(text="Notes"))
|
|
header.append(blank)
|
|
|
|
summaryhead.append(header)
|
|
|
|
grades = matrix.grades
|
|
impls = list(matrix.backends.keys())
|
|
impls.sort()
|
|
for grade in grades:
|
|
for backend in impls:
|
|
if matrix.backends[backend].status == grade.key:
|
|
item = nodes.row()
|
|
namecol = nodes.entry()
|
|
namecol.append(
|
|
nodes.paragraph(text=matrix.backends[backend].title))
|
|
item.append(namecol)
|
|
|
|
statuscol = nodes.entry()
|
|
class_name = "label-%s" % grade.css_class
|
|
status_text = nodes.paragraph(text=grade.title)
|
|
status_text['classes'].append(class_name)
|
|
status_text['classes'].append("label")
|
|
statuscol.append(status_text)
|
|
item.append(statuscol)
|
|
|
|
typecol = nodes.entry()
|
|
type_text = nodes.paragraph(
|
|
text=matrix.backends[backend].type)
|
|
type_text['classes'].append("label")
|
|
type_text['classes'].append("label-info")
|
|
typecol.append(type_text)
|
|
item.append(typecol)
|
|
|
|
if bool(matrix.backends[backend].in_tree):
|
|
status = "\u2714"
|
|
intree = nodes.paragraph(text=status)
|
|
intree['classes'].append("label")
|
|
intree['classes'].append("label-success")
|
|
|
|
else:
|
|
status = "\u2716"
|
|
intree = nodes.paragraph(text=status)
|
|
intree['classes'].append("label")
|
|
intree['classes'].append("label-danger")
|
|
|
|
intreecol = nodes.entry()
|
|
intreecol.append(intree)
|
|
item.append(intreecol)
|
|
|
|
notescol = nodes.entry()
|
|
notescol.append(nodes.paragraph(
|
|
text=matrix.backends[backend].notes))
|
|
item.append(notescol)
|
|
|
|
summarybody.append(item)
|
|
|
|
return content
|
|
|
|
|
|
def copy_assets(app, exception):
|
|
assets = ['support-matrix.css', 'support-matrix.js']
|
|
if app.builder.name != 'html' or exception:
|
|
return
|
|
LOG.info('Copying assets: %s' % ', '.join(assets))
|
|
for asset in assets:
|
|
dest = os.path.join(app.builder.outdir, '_static', asset)
|
|
source = os.path.abspath(os.path.dirname(__file__))
|
|
copyfile(os.path.join(source, 'assets', asset), dest)
|
|
|
|
|
|
def add_assets(app):
|
|
app.add_css_file('support-matrix.css')
|
|
app.add_js_file('support-matrix.js')
|
|
|
|
|
|
def setup(app):
|
|
|
|
# Add all the static assets to our build during the early stage of building
|
|
app.connect('builder-inited', add_assets)
|
|
|
|
# This copies all the assets (css, js, fonts) over to the build
|
|
# _static directory during final build.
|
|
app.connect('build-finished', copy_assets)
|
|
|
|
app.add_directive('support_matrix', SupportMatrixDirective)
|