You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
404 lines
14 KiB
404 lines
14 KiB
# |
|
# 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. |
|
# -*- coding: utf-8 -*- |
|
|
|
import itertools |
|
|
|
from docutils import core |
|
from docutils import nodes |
|
import pydoc |
|
import six |
|
from sphinx.util import compat |
|
|
|
from heat.common.i18n import _ |
|
from heat.engine import attributes |
|
from heat.engine import plugin_manager |
|
from heat.engine import properties |
|
from heat.engine import support |
|
|
|
_CODE_NAMES = {'2013.1': 'Grizzly', |
|
'2013.2': 'Havana', |
|
'2014.1': 'Icehouse', |
|
'2014.2': 'Juno', |
|
'2015.1': 'Kilo', |
|
'5.0.0': 'Liberty'} |
|
|
|
all_resources = {} |
|
|
|
|
|
class integratedrespages(nodes.General, nodes.Element): |
|
pass |
|
|
|
|
|
class unsupportedrespages(nodes.General, nodes.Element): |
|
pass |
|
|
|
|
|
class contribresourcepages(nodes.General, nodes.Element): |
|
pass |
|
|
|
|
|
class ResourcePages(compat.Directive): |
|
has_content = False |
|
required_arguments = 0 |
|
optional_arguments = 1 |
|
final_argument_whitespace = False |
|
option_spec = {} |
|
|
|
def path(self): |
|
return None |
|
|
|
def statuses(self): |
|
return support.SUPPORT_STATUSES |
|
|
|
def run(self): |
|
prefix = self.arguments and self.arguments.pop() or None |
|
content = [] |
|
for resource_type, resource_classes in _filter_resources( |
|
prefix, self.path(), self.statuses()): |
|
for resource_class in resource_classes: |
|
self.resource_type = resource_type |
|
self.resource_class = resource_class |
|
section = self._section(content, resource_type, '%s') |
|
|
|
self.props_schemata = properties.schemata( |
|
self.resource_class.properties_schema) |
|
self.attrs_schemata = attributes.schemata( |
|
self.resource_class.attributes_schema) |
|
self.update_policy_schemata = properties.schemata( |
|
self.resource_class.update_policy_schema) |
|
|
|
|
|
self._status_str(resource_class.support_status, section) |
|
|
|
cls_doc = pydoc.getdoc(resource_class) |
|
if cls_doc: |
|
# allow for rst in the class comments |
|
cls_nodes = core.publish_doctree(cls_doc).children |
|
section.extend(cls_nodes) |
|
|
|
self.contribute_properties(section) |
|
self.contribute_attributes(section) |
|
self.contribute_update_policy(section) |
|
|
|
self.contribute_hot_syntax(section) |
|
|
|
return content |
|
|
|
def _version_str(self, version): |
|
if version in _CODE_NAMES: |
|
return _("%(version)s (%(code)s)") % {'version': version, |
|
'code': _CODE_NAMES[version]} |
|
else: |
|
return version |
|
|
|
def _status_str(self, support_status, section): |
|
while support_status is not None: |
|
sstatus = support_status.to_dict() |
|
if sstatus['status'] is support.SUPPORTED: |
|
msg = _('Available') |
|
else: |
|
msg = sstatus['status'] |
|
if sstatus['version'] is not None: |
|
msg = _('%s since %s') % (msg, |
|
self._version_str( |
|
sstatus['version'])) |
|
if sstatus['message'] is not None: |
|
msg = _('%s - %s') % (msg, sstatus['message']) |
|
if not (sstatus['status'] == support.SUPPORTED and |
|
sstatus['version'] is None): |
|
para = nodes.paragraph(_(''), msg) |
|
note = nodes.note(_(''), para) |
|
section.append(note) |
|
support_status = support_status.previous_status |
|
|
|
def _section(self, parent, title, id_pattern): |
|
id = id_pattern % self.resource_type |
|
section = nodes.section(ids=[id]) |
|
parent.append(section) |
|
title = nodes.title('', title) |
|
section.append(title) |
|
return section |
|
|
|
def _prop_syntax_example(self, prop): |
|
if not prop: |
|
return 'Value' |
|
if prop.type == properties.Schema.LIST: |
|
schema = lambda i: prop.schema[i] if prop.schema else None |
|
sub_type = [self._prop_syntax_example(schema(i)) |
|
for i in range(2)] |
|
return '[%s, %s, ...]' % tuple(sub_type) |
|
elif prop.type == properties.Schema.MAP: |
|
def sub_props(): |
|
for sub_key, sub_value in prop.schema.items(): |
|
if sub_value.implemented: |
|
yield '"%s": %s' % ( |
|
sub_key, self._prop_syntax_example(sub_value)) |
|
return '{%s}' % (', '.join(sub_props()) if prop.schema else '...') |
|
else: |
|
return prop.type |
|
|
|
def contribute_hot_syntax(self, parent): |
|
section = self._section(parent, _('HOT Syntax'), '%s-hot') |
|
props = [] |
|
for prop_key in sorted(six.iterkeys(self.props_schemata)): |
|
prop = self.props_schemata[prop_key] |
|
if (prop.implemented |
|
and prop.support_status.status == support.SUPPORTED): |
|
props.append('%s: %s' % (prop_key, |
|
self._prop_syntax_example(prop))) |
|
|
|
props_str = '' |
|
if props: |
|
props_str = '''\n properties: |
|
%s''' % ('\n '.join(props)) |
|
|
|
template = '''heat_template_version: 2013-05-23 |
|
... |
|
resources: |
|
... |
|
the_resource: |
|
type: %s%s''' % (self.resource_type, props_str) |
|
|
|
block = nodes.literal_block('', template, language="hot") |
|
section.append(block) |
|
|
|
@staticmethod |
|
def cmp_prop(x, y): |
|
x_key, x_prop = x |
|
y_key, y_prop = y |
|
if x_prop.support_status.status == y_prop.support_status.status: |
|
return cmp(x_key, y_key) |
|
if x_prop.support_status.status == support.SUPPORTED: |
|
return -1 |
|
if x_prop.support_status.status == support.DEPRECATED: |
|
return 1 |
|
return cmp(x_prop.support_status.status, |
|
y_prop.support_status.status) |
|
|
|
def contribute_property(self, prop_list, prop_key, prop): |
|
prop_item = nodes.definition_list_item( |
|
'', nodes.term('', prop_key)) |
|
prop_list.append(prop_item) |
|
|
|
prop_item.append(nodes.classifier('', prop.type)) |
|
|
|
definition = nodes.definition() |
|
prop_item.append(definition) |
|
|
|
self._status_str(prop.support_status, definition) |
|
|
|
if not prop.implemented: |
|
para = nodes.paragraph('', _('Not implemented.')) |
|
note = nodes.note('', para) |
|
definition.append(note) |
|
return |
|
|
|
if prop.description: |
|
para = nodes.paragraph('', prop.description) |
|
definition.append(para) |
|
|
|
if prop.update_allowed: |
|
para = nodes.paragraph('', |
|
_('Can be updated without replacement.')) |
|
definition.append(para) |
|
elif prop.immutable: |
|
para = nodes.paragraph('', _('Updates are not supported. ' |
|
'Resource update will fail on any ' |
|
'attempt to update this property.')) |
|
definition.append(para) |
|
else: |
|
para = nodes.paragraph('', _('Updates cause replacement.')) |
|
definition.append(para) |
|
|
|
if prop.required: |
|
para = nodes.paragraph('', _('Required property.')) |
|
elif prop.default is not None: |
|
para = nodes.paragraph( |
|
'', |
|
_('Optional property, defaults to "%s".') % prop.default) |
|
else: |
|
para = nodes.paragraph('', _('Optional property.')) |
|
definition.append(para) |
|
|
|
for constraint in prop.constraints: |
|
para = nodes.paragraph('', str(constraint)) |
|
definition.append(para) |
|
|
|
sub_schema = None |
|
if prop.schema and prop.type == properties.Schema.MAP: |
|
para = nodes.paragraph() |
|
emph = nodes.emphasis('', _('Map properties:')) |
|
para.append(emph) |
|
definition.append(para) |
|
sub_schema = prop.schema |
|
|
|
elif prop.schema and prop.type == properties.Schema.LIST: |
|
para = nodes.paragraph() |
|
emph = nodes.emphasis('', _('List contents:')) |
|
para.append(emph) |
|
definition.append(para) |
|
sub_schema = prop.schema |
|
|
|
if sub_schema: |
|
sub_prop_list = nodes.definition_list() |
|
definition.append(sub_prop_list) |
|
for sub_prop_key, sub_prop in sorted(sub_schema.items(), |
|
self.cmp_prop): |
|
if sub_prop.support_status.status != support.HIDDEN: |
|
self.contribute_property( |
|
sub_prop_list, sub_prop_key, sub_prop) |
|
|
|
def contribute_properties(self, parent): |
|
if not self.props_schemata: |
|
return |
|
section = self._section(parent, _('Properties'), '%s-props') |
|
prop_list = nodes.definition_list() |
|
section.append(prop_list) |
|
|
|
for prop_key, prop in sorted(self.props_schemata.items(), |
|
self.cmp_prop): |
|
if prop.support_status.status != support.HIDDEN: |
|
self.contribute_property(prop_list, prop_key, prop) |
|
|
|
def contribute_attributes(self, parent): |
|
if not self.attrs_schemata: |
|
return |
|
section = self._section(parent, _('Attributes'), '%s-attrs') |
|
prop_list = nodes.definition_list() |
|
section.append(prop_list) |
|
for prop_key, prop in sorted(self.attrs_schemata.items()): |
|
if prop.support_status.status != support.HIDDEN: |
|
description = prop.description |
|
prop_item = nodes.definition_list_item( |
|
'', nodes.term('', prop_key)) |
|
prop_list.append(prop_item) |
|
|
|
definition = nodes.definition() |
|
prop_item.append(definition) |
|
|
|
self._status_str(prop.support_status, definition) |
|
|
|
if description: |
|
def_para = nodes.paragraph('', description) |
|
definition.append(def_para) |
|
|
|
def contribute_update_policy(self, parent): |
|
if not self.update_policy_schemata: |
|
return |
|
section = self._section(parent, _('UpdatePolicy'), '%s-updpolicy') |
|
prop_list = nodes.definition_list() |
|
section.append(prop_list) |
|
for prop_key, prop in sorted(self.update_policy_schemata.items(), |
|
self.cmp_prop): |
|
self.contribute_property(prop_list, prop_key, prop) |
|
|
|
|
|
class IntegrateResourcePages(ResourcePages): |
|
|
|
def path(self): |
|
return 'heat.engine.resources' |
|
|
|
def statuses(self): |
|
return [support.SUPPORTED] |
|
|
|
|
|
class UnsupportedResourcePages(ResourcePages): |
|
|
|
def path(self): |
|
return 'heat.engine.resources' |
|
|
|
def statuses(self): |
|
return [s for s in support.SUPPORT_STATUSES if s != support.SUPPORTED] |
|
|
|
|
|
class ContribResourcePages(ResourcePages): |
|
|
|
def path(self): |
|
return 'heat.engine.plugins' |
|
|
|
|
|
def _filter_resources(prefix=None, path=None, statuses=[]): |
|
|
|
def not_hidden_match(cls): |
|
return cls.support_status.status != support.HIDDEN |
|
|
|
def prefix_match(name): |
|
return prefix is None or name.startswith(prefix) |
|
|
|
def path_match(cls): |
|
return path is None or cls.__module__.startswith(path) |
|
|
|
def status_match(cls): |
|
return cls.support_status.status in statuses |
|
|
|
filtered_resources = {} |
|
for name in sorted(six.iterkeys(all_resources)): |
|
if prefix_match(name): |
|
for cls in all_resources.get(name): |
|
if (path_match(cls) and status_match(cls) and |
|
not_hidden_match(cls)): |
|
if filtered_resources.get(name) is not None: |
|
filtered_resources[name].append(cls) |
|
else: |
|
filtered_resources[name] = [cls] |
|
|
|
return sorted(six.iteritems(filtered_resources)) |
|
|
|
|
|
def _load_all_resources(): |
|
manager = plugin_manager.PluginManager('heat.engine.resources') |
|
resource_mapping = plugin_manager.PluginMapping('resource') |
|
res_plugin_mappings = resource_mapping.load_all(manager) |
|
|
|
for mapping in res_plugin_mappings: |
|
name, cls = mapping |
|
if all_resources.get(name) is not None: |
|
all_resources[name].append(cls) |
|
else: |
|
all_resources[name] = [cls] |
|
|
|
|
|
def link_resource(app, env, node, contnode): |
|
reftarget = node.attributes['reftarget'] |
|
for resource_name in all_resources: |
|
if resource_name.lower() == reftarget.lower(): |
|
resource = all_resources[resource_name] |
|
refnode = nodes.reference('', '', internal=True) |
|
refnode['reftitle'] = resource_name |
|
if resource_name.startswith('AWS'): |
|
source = 'template_guide/cfn' |
|
else: |
|
source = 'template_guide/openstack' |
|
uri = app.builder.get_relative_uri( |
|
node.attributes['refdoc'], source) |
|
refnode['refuri'] = '%s#%s' % (uri, resource_name) |
|
refnode.append(contnode) |
|
return refnode |
|
|
|
|
|
def setup(app): |
|
_load_all_resources() |
|
app.add_node(integratedrespages) |
|
|
|
app.add_directive('integratedrespages', IntegrateResourcePages) |
|
|
|
app.add_node(unsupportedrespages) |
|
|
|
app.add_directive('unsupportedrespages', UnsupportedResourcePages) |
|
|
|
app.add_node(contribresourcepages) |
|
|
|
app.add_directive('contribrespages', ContribResourcePages) |
|
|
|
app.connect('missing-reference', link_resource)
|
|
|