OpenStack Orchestration (Heat)
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

#
# 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)