f4c249c692
Introduce two new guides on UEFI and Secure Boot. In addition, update the flavors guide to document the secure boot feature (though this doc should really be removed in near term in favour of the auto-generated docs, as noted inline). Note that this change includes our first use of the ':nova:extra-spec:' cross-reference role and highlights a small bug in that implementation. This is resolved. Blueprint: allow-secure-boot-for-qemu-kvm-guests Change-Id: I4eb370b87ba8d0403c8c0ef038a909313a48d1d6 Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
240 lines
6.9 KiB
Python
240 lines
6.9 KiB
Python
# Copyright 2020, 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.
|
|
|
|
"""Display extra specs in documentation.
|
|
|
|
Provides a single directive that can be used to list all extra specs validators
|
|
and, thus, document all extra specs that nova recognizes and supports.
|
|
"""
|
|
|
|
import typing as ty
|
|
|
|
from docutils import nodes
|
|
from docutils.parsers import rst
|
|
from docutils.parsers.rst import directives
|
|
from docutils import statemachine
|
|
from sphinx import addnodes
|
|
from sphinx import directives as sphinx_directives
|
|
from sphinx import domains
|
|
from sphinx import roles
|
|
from sphinx.util import logging
|
|
from sphinx.util import nodes as sphinx_nodes
|
|
|
|
from nova.api.validation.extra_specs import base
|
|
from nova.api.validation.extra_specs import validators
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class ExtraSpecXRefRole(roles.XRefRole):
|
|
"""Cross reference a extra spec.
|
|
|
|
Example::
|
|
|
|
:nova:extra-spec:`hw:cpu_policy`
|
|
"""
|
|
|
|
def __init__(self):
|
|
super(ExtraSpecXRefRole, self).__init__(
|
|
warn_dangling=True,
|
|
)
|
|
|
|
def process_link(self, env, refnode, has_explicit_title, title, target):
|
|
# The anchor for the extra spec link is the extra spec name
|
|
return target, target
|
|
|
|
|
|
class ExtraSpecDirective(sphinx_directives.ObjectDescription):
|
|
"""Document an individual extra spec.
|
|
|
|
Accepts one required argument - the extra spec name, including the group.
|
|
|
|
Example::
|
|
|
|
.. extra-spec:: hw:cpu_policy
|
|
"""
|
|
|
|
def handle_signature(self, sig, signode):
|
|
"""Transform an option description into RST nodes."""
|
|
# Insert a node into the output showing the extra spec name
|
|
signode += addnodes.desc_name(sig, sig)
|
|
signode['allnames'] = [sig]
|
|
return sig
|
|
|
|
def add_target_and_index(self, firstname, sig, signode):
|
|
cached_options = self.env.domaindata['nova']['extra_specs']
|
|
signode['ids'].append(sig)
|
|
self.state.document.note_explicit_target(signode)
|
|
# Store the location of the option definition for later use in
|
|
# resolving cross-references
|
|
cached_options[sig] = self.env.docname
|
|
|
|
|
|
def _indent(text, count=1):
|
|
if not text:
|
|
return text
|
|
|
|
padding = ' ' * (4 * count)
|
|
return padding + text
|
|
|
|
|
|
def _format_validator_group_help(
|
|
validators: ty.Dict[str, base.ExtraSpecValidator],
|
|
summary: bool,
|
|
):
|
|
"""Generate reStructuredText snippets for a group of validators."""
|
|
for validator in validators.values():
|
|
for line in _format_validator_help(validator, summary):
|
|
yield line
|
|
|
|
|
|
def _format_validator_help(
|
|
validator: base.ExtraSpecValidator,
|
|
summary: bool,
|
|
):
|
|
"""Generate reStucturedText snippets for the provided validator.
|
|
|
|
:param validator: A validator to document.
|
|
:type validator: nova.api.validation.extra_specs.base.ExtraSpecValidator
|
|
"""
|
|
yield f'.. nova:extra-spec:: {validator.name}'
|
|
yield ''
|
|
|
|
# NOTE(stephenfin): We don't print the pattern, if present, since it's too
|
|
# internal. Instead, the description should provide this information in a
|
|
# human-readable format
|
|
yield _indent(f':Type: {validator.value["type"].__name__}')
|
|
|
|
if validator.value.get('min') is not None:
|
|
yield _indent(f':Min: {validator.value["min"]}')
|
|
|
|
if validator.value.get('max') is not None:
|
|
yield _indent(f':Max: {validator.value["max"]}')
|
|
|
|
yield ''
|
|
|
|
if not summary:
|
|
for line in validator.description.splitlines():
|
|
yield _indent(line)
|
|
|
|
yield ''
|
|
|
|
if validator.deprecated:
|
|
yield _indent('.. warning::')
|
|
yield _indent(
|
|
'This extra spec has been deprecated and should not be used.', 2
|
|
)
|
|
yield ''
|
|
|
|
|
|
class ExtraSpecGroupDirective(rst.Directive):
|
|
"""Document extra specs belonging to the specified group.
|
|
|
|
Accepts one optional argument - the extra spec group - and one option -
|
|
whether to show a summary view only (omit descriptions). Example::
|
|
|
|
.. extra-specs:: hw_rng
|
|
:summary:
|
|
"""
|
|
|
|
required_arguments = 0
|
|
optional_arguments = 1
|
|
option_spec = {
|
|
'summary': directives.flag,
|
|
}
|
|
has_content = False
|
|
|
|
def run(self):
|
|
result = statemachine.ViewList()
|
|
source_name = self.state.document.current_source
|
|
|
|
group = self.arguments[0] if self.arguments else None
|
|
summary = self.options.get('summary', False)
|
|
|
|
if group:
|
|
group_validators = {
|
|
n.split(':', 1)[1]: v for n, v in validators.VALIDATORS.items()
|
|
if ':' in n and n.split(':', 1)[0].split('{')[0] == group
|
|
}
|
|
else:
|
|
group_validators = {
|
|
n: v for n, v in validators.VALIDATORS.items()
|
|
if ':' not in n
|
|
}
|
|
|
|
if not group_validators:
|
|
LOG.warning("No validators found for group '%s'", group or '')
|
|
|
|
for count, line in enumerate(
|
|
_format_validator_group_help(group_validators, summary)
|
|
):
|
|
result.append(line, source_name, count)
|
|
LOG.debug('%5d%s%s', count, ' ' if line else '', line)
|
|
|
|
node = nodes.section()
|
|
node.document = self.state.document
|
|
|
|
sphinx_nodes.nested_parse_with_titles(self.state, result, node)
|
|
|
|
return node.children
|
|
|
|
|
|
class NovaDomain(domains.Domain):
|
|
"""nova domain."""
|
|
name = 'nova'
|
|
label = 'nova'
|
|
object_types = {
|
|
'configoption': domains.ObjType(
|
|
'extra spec', 'spec',
|
|
),
|
|
}
|
|
directives = {
|
|
'extra-spec': ExtraSpecDirective,
|
|
}
|
|
roles = {
|
|
'extra-spec': ExtraSpecXRefRole(),
|
|
}
|
|
initial_data = {
|
|
'extra_specs': {},
|
|
}
|
|
|
|
def resolve_xref(
|
|
self, env, fromdocname, builder, typ, target, node, contnode,
|
|
):
|
|
"""Resolve cross-references"""
|
|
if typ == 'extra-spec':
|
|
return sphinx_nodes.make_refnode(
|
|
builder,
|
|
fromdocname,
|
|
env.domaindata['nova']['extra_specs'][target],
|
|
target,
|
|
contnode,
|
|
target,
|
|
)
|
|
return None
|
|
|
|
def merge_domaindata(self, docnames, otherdata):
|
|
for target, docname in otherdata['extra_specs'].items():
|
|
if docname in docnames:
|
|
self.data['extra_specs'][target] = docname
|
|
|
|
|
|
def setup(app):
|
|
app.add_domain(NovaDomain)
|
|
app.add_directive('extra-specs', ExtraSpecGroupDirective)
|
|
return {
|
|
'parallel_read_safe': True,
|
|
'parallel_write_safe': True,
|
|
}
|