Move HOT template code to its own module

Change-Id: Id491feb251db7a42eddd6dcdcc2f68628e6f8c22
This commit is contained in:
Zane Bitter 2014-03-06 10:05:01 -05:00
parent ffe0af0e94
commit 4f20c532e3
5 changed files with 151 additions and 156 deletions

View File

@ -1,152 +0,0 @@
#
# 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 heat.engine import template
from heat.engine.cfn import template as cfn_template
from heat.engine.hot import parameters
from heat.openstack.common.gettextutils import _
from heat.openstack.common import log as logging
logger = logging.getLogger(__name__)
def snake_to_camel(name):
return ''.join([t.capitalize() for t in name.split('_')])
class HOTemplate(template.Template):
"""
A Heat Orchestration Template format stack template.
"""
SECTIONS = (VERSION, DESCRIPTION, PARAMETER_GROUPS, PARAMETERS,
RESOURCES, OUTPUTS, MAPPINGS) = \
('heat_template_version', 'description', 'parameter_groups',
'parameters', 'resources', 'outputs', '__undefined__')
SECTIONS_NO_DIRECT_ACCESS = set([PARAMETERS, VERSION])
VERSIONS = ('2013-05-23',)
_CFN_TO_HOT_SECTIONS = {cfn_template.CfnTemplate.VERSION: VERSION,
cfn_template.CfnTemplate.DESCRIPTION: DESCRIPTION,
cfn_template.CfnTemplate.PARAMETERS: PARAMETERS,
cfn_template.CfnTemplate.MAPPINGS: MAPPINGS,
cfn_template.CfnTemplate.RESOURCES: RESOURCES,
cfn_template.CfnTemplate.OUTPUTS: OUTPUTS}
def __init__(self, template, *args, **kwargs):
# All user templates are forced to include a version string. This is
# just a convenient default for unit tests.
version = template.get(self.VERSION, '2013-05-23')
if version not in self.VERSIONS:
msg = _('"%(version)s" is not a valid '
'heat_template_version. Should be one of: '
'%(valid)s')
raise ValueError(msg % {'version': version,
'valid': str(self.VERSIONS)})
super(HOTemplate, self).__init__(template, *args, **kwargs)
self.version = self.VERSION, version
def __getitem__(self, section):
""""Get the relevant section in the template."""
#first translate from CFN into HOT terminology if necessary
section = HOTemplate._translate(section,
self._CFN_TO_HOT_SECTIONS, section)
if section not in self.SECTIONS:
raise KeyError(_('"%s" is not a valid template section') % section)
if section in self.SECTIONS_NO_DIRECT_ACCESS:
raise KeyError(
_('Section %s can not be accessed directly.') % section)
if section == self.MAPPINGS:
return {}
if section == self.DESCRIPTION:
default = 'No description'
else:
default = {}
the_section = self.t.get(section, default)
# In some cases (e.g. parameters), also translate each entry of
# a section into CFN format (case, naming, etc) so the rest of the
# engine can cope with it.
# This is a shortcut for now and might be changed in the future.
if section == self.RESOURCES:
return self._translate_resources(the_section)
if section == self.OUTPUTS:
return self._translate_outputs(the_section)
return the_section
@staticmethod
def _translate(value, mapping, default=None):
if value in mapping:
return mapping[value]
return default
def _translate_resources(self, resources):
"""Get the resources of the template translated into CFN format."""
HOT_TO_CFN_ATTRS = {'type': 'Type',
'properties': 'Properties'}
cfn_resources = {}
for resource_name, attrs in resources.iteritems():
cfn_resource = {}
for attr, attr_value in attrs.iteritems():
cfn_attr = self._translate(attr, HOT_TO_CFN_ATTRS, attr)
cfn_resource[cfn_attr] = attr_value
cfn_resources[resource_name] = cfn_resource
return cfn_resources
def _translate_outputs(self, outputs):
"""Get the outputs of the template translated into CFN format."""
HOT_TO_CFN_ATTRS = {'description': 'Description',
'value': 'Value'}
cfn_outputs = {}
for output_name, attrs in outputs.iteritems():
cfn_output = {}
for attr, attr_value in attrs.iteritems():
cfn_attr = self._translate(attr, HOT_TO_CFN_ATTRS, attr)
cfn_output[cfn_attr] = attr_value
cfn_outputs[output_name] = cfn_output
return cfn_outputs
def param_schemata(self):
params = self.t.get(self.PARAMETERS, {}).iteritems()
return dict((name, parameters.HOTParamSchema.from_dict(schema))
for name, schema in params)
def parameters(self, stack_identifier, user_params, validate_value=True,
context=None):
return parameters.HOTParameters(stack_identifier, self,
user_params=user_params,
validate_value=validate_value,
context=context)

147
heat/engine/hot/template.py Normal file
View File

@ -0,0 +1,147 @@
# 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 heat.engine import template
from heat.engine.cfn import template as cfn_template
from heat.engine.hot import parameters
from heat.openstack.common.gettextutils import _
from heat.openstack.common import log as logging
logger = logging.getLogger(__name__)
class HOTemplate(template.Template):
"""
A Heat Orchestration Template format stack template.
"""
SECTIONS = (VERSION, DESCRIPTION, PARAMETER_GROUPS, PARAMETERS,
RESOURCES, OUTPUTS, MAPPINGS) = \
('heat_template_version', 'description', 'parameter_groups',
'parameters', 'resources', 'outputs', '__undefined__')
SECTIONS_NO_DIRECT_ACCESS = set([PARAMETERS, VERSION])
VERSIONS = ('2013-05-23',)
_CFN_TO_HOT_SECTIONS = {cfn_template.CfnTemplate.VERSION: VERSION,
cfn_template.CfnTemplate.DESCRIPTION: DESCRIPTION,
cfn_template.CfnTemplate.PARAMETERS: PARAMETERS,
cfn_template.CfnTemplate.MAPPINGS: MAPPINGS,
cfn_template.CfnTemplate.RESOURCES: RESOURCES,
cfn_template.CfnTemplate.OUTPUTS: OUTPUTS}
def __init__(self, template, *args, **kwargs):
# All user templates are forced to include a version string. This is
# just a convenient default for unit tests.
version = template.get(self.VERSION, '2013-05-23')
if version not in self.VERSIONS:
msg = _('"%(version)s" is not a valid '
'heat_template_version. Should be one of: '
'%(valid)s')
raise ValueError(msg % {'version': version,
'valid': str(self.VERSIONS)})
super(HOTemplate, self).__init__(template, *args, **kwargs)
self.version = self.VERSION, version
def __getitem__(self, section):
""""Get the relevant section in the template."""
#first translate from CFN into HOT terminology if necessary
section = HOTemplate._translate(section,
self._CFN_TO_HOT_SECTIONS, section)
if section not in self.SECTIONS:
raise KeyError(_('"%s" is not a valid template section') % section)
if section in self.SECTIONS_NO_DIRECT_ACCESS:
raise KeyError(
_('Section %s can not be accessed directly.') % section)
if section == self.MAPPINGS:
return {}
if section == self.DESCRIPTION:
default = 'No description'
else:
default = {}
the_section = self.t.get(section, default)
# In some cases (e.g. parameters), also translate each entry of
# a section into CFN format (case, naming, etc) so the rest of the
# engine can cope with it.
# This is a shortcut for now and might be changed in the future.
if section == self.RESOURCES:
return self._translate_resources(the_section)
if section == self.OUTPUTS:
return self._translate_outputs(the_section)
return the_section
@staticmethod
def _translate(value, mapping, default=None):
if value in mapping:
return mapping[value]
return default
def _translate_resources(self, resources):
"""Get the resources of the template translated into CFN format."""
HOT_TO_CFN_ATTRS = {'type': 'Type',
'properties': 'Properties'}
cfn_resources = {}
for resource_name, attrs in resources.iteritems():
cfn_resource = {}
for attr, attr_value in attrs.iteritems():
cfn_attr = self._translate(attr, HOT_TO_CFN_ATTRS, attr)
cfn_resource[cfn_attr] = attr_value
cfn_resources[resource_name] = cfn_resource
return cfn_resources
def _translate_outputs(self, outputs):
"""Get the outputs of the template translated into CFN format."""
HOT_TO_CFN_ATTRS = {'description': 'Description',
'value': 'Value'}
cfn_outputs = {}
for output_name, attrs in outputs.iteritems():
cfn_output = {}
for attr, attr_value in attrs.iteritems():
cfn_attr = self._translate(attr, HOT_TO_CFN_ATTRS, attr)
cfn_output[cfn_attr] = attr_value
cfn_outputs[output_name] = cfn_output
return cfn_outputs
def param_schemata(self):
params = self.t.get(self.PARAMETERS, {}).iteritems()
return dict((name, parameters.HOTParamSchema.from_dict(schema))
for name, schema in params)
def parameters(self, stack_identifier, user_params, validate_value=True,
context=None):
return parameters.HOTParameters(stack_identifier, self,
user_params=user_params,
validate_value=validate_value,
context=context)

View File

@ -60,7 +60,7 @@ class Template(collections.Mapping):
if cls == Template: if cls == Template:
# deferred module imports to avoid circular dependency # deferred module imports to avoid circular dependency
if 'heat_template_version' in template: if 'heat_template_version' in template:
from heat.engine import hot from heat.engine.hot import template as hot
return hot.HOTemplate(template, *args, **kwargs) return hot.HOTemplate(template, *args, **kwargs)
else: else:
from heat.engine.cfn import template as cfn from heat.engine.cfn import template as cfn

View File

@ -17,8 +17,8 @@ from heat.engine import environment
from heat.engine import function from heat.engine import function
from heat.engine import parser from heat.engine import parser
from heat.engine import resource from heat.engine import resource
from heat.engine import hot
from heat.engine.hot import parameters as hot_param from heat.engine.hot import parameters as hot_param
from heat.engine.hot import template as hot_template
from heat.engine import resources from heat.engine import resources
from heat.engine import template from heat.engine import template
from heat.engine import constraints from heat.engine import constraints
@ -74,7 +74,7 @@ class HOTemplateTest(HeatTestCase):
tmpl = parser.Template(hot_tpl_empty) tmpl = parser.Template(hot_tpl_empty)
# check if we get the right class # check if we get the right class
self.assertIsInstance(tmpl, hot.HOTemplate) self.assertIsInstance(tmpl, hot_template.HOTemplate)
# test getting an invalid section # test getting an invalid section
self.assertNotIn('foobar', tmpl) self.assertNotIn('foobar', tmpl)

View File

@ -25,7 +25,7 @@ from heat.engine.resources import instance as instances
from heat.engine import service from heat.engine import service
from heat.openstack.common.importutils import try_import from heat.openstack.common.importutils import try_import
from heat.engine import parser from heat.engine import parser
from heat.engine.hot import HOTemplate from heat.engine.hot.template import HOTemplate
from heat.tests.common import HeatTestCase from heat.tests.common import HeatTestCase
from heat.tests import utils from heat.tests import utils