From: Angus Salkeld Date: Mon, 24 Jun 2013 01:18:09 +0000 (+1000) Subject: Initial provider templates X-Git-Tag: 2014.1~429^2 X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=351830d10247f52457a157e05d461efb17238592;p=openstack-build%2Fheat-build.git Initial provider templates this is the "lite" version of blueprint provider-resource Change-Id: Ife706c932b6b9f0bcf57fb907b369231c226c7c4 --- diff --git a/heat/engine/resource.py b/heat/engine/resource.py index a77c20ea..35704520 100644 --- a/heat/engine/resource.py +++ b/heat/engine/resource.py @@ -33,6 +33,7 @@ logger = logging.getLogger(__name__) _resource_classes = {} +_template_class = None def get_types(): @@ -45,7 +46,11 @@ def get_class(resource_type, resource_name=None, environment=None): if environment: resource_type = environment.get_resource_type(resource_type, resource_name) - cls = _resource_classes.get(resource_type) + + if resource_type.endswith(('.yaml', '.template')): + cls = _template_class + else: + cls = _resource_classes.get(resource_type) if cls is None: msg = "Unknown resource Type : %s" % resource_type raise exception.StackValidationFailed(message=msg) @@ -62,6 +67,12 @@ def _register_class(resource_type, resource_class): _resource_classes[resource_type] = resource_class +def register_template_class(cls): + global _template_class + if _template_class is None: + _template_class = cls + + class UpdateReplace(Exception): ''' Raised when resource update requires replacement diff --git a/heat/engine/resources/template_resource.py b/heat/engine/resources/template_resource.py new file mode 100644 index 00000000..1eab5ad1 --- /dev/null +++ b/heat/engine/resources/template_resource.py @@ -0,0 +1,76 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# +# 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 resource +from heat.engine import stack_resource +from heat.engine import properties +from heat.common import template_format + +from heat.openstack.common import log as logging + +logger = logging.getLogger(__name__) + + +class TemplateResource(stack_resource.StackResource): + '''A Nested Stack Resource representing another Resource.''' + def __init__(self, name, json_snippet, stack): + self.template_name = stack.env.get_resource_type(json_snippet['Type'], + name) + # on purpose don't pass in the environment so we get + # the official/facade class to copy it's schema. + cls_facade = resource.get_class(json_snippet['Type']) + self.properties_schema = cls_facade.properties_schema + self.attributes_schema = cls_facade.attributes_schema + + super(TemplateResource, self).__init__(name, json_snippet, stack) + + def _to_parameters(self): + '''Convert CommaDelimitedList to List.''' + # TODO(asalkeld or randall) + params = {} + for n, v in iter(self.properties.props.items()): + if not v.implemented(): + continue + if v.type() == properties.MAP: + # totally ignore maps for now. + logger.warn('%s ignoring property %s' % (self.name, n)) + elif v.type() == properties.LIST: + # take a list and create a CommaDelimitedList + val = self.properties[n] + if val: + params[n] = ','.join(val) + else: + params[n] = self.properties[n] + + return params + + def handle_create(self): + template_data = self.stack.t.files.get(self.template_name) + template = template_format.parse(template_data) + + return self.create_with_template(template, + self._to_parameters()) + + def handle_delete(self): + self.delete_nested() + + def FnGetRefId(self): + return self.nested().identifier().arn() + + def FnGetAtt(self, key): + return unicode(self.get_output(key)) + + +resource.register_template_class(TemplateResource) diff --git a/heat/tests/test_provider_template.py b/heat/tests/test_provider_template.py new file mode 100644 index 00000000..94560b6c --- /dev/null +++ b/heat/tests/test_provider_template.py @@ -0,0 +1,82 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# 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 environment +from heat.engine import resource +from heat.engine.resources import template_resource + +from heat.tests import generic_resource as generic_rsrc +from heat.tests.common import HeatTestCase + + +class MyCloudResource(generic_rsrc.GenericResource): + pass + + +class ProviderTemplateTest(HeatTestCase): + def setUp(self): + super(ProviderTemplateTest, self).setUp() + resource._register_class('OS::ResourceType', + generic_rsrc.GenericResource) + resource._register_class('myCloud::ResourceType', + MyCloudResource) + + def test_get_os_empty_registry(self): + # assertion: with an empty environment we get the correct + # default class. + env_str = {'resource_registry': {}} + env = environment.Environment(env_str) + cls = resource.get_class('OS::ResourceType', 'fred', env) + self.assertEqual(cls, generic_rsrc.GenericResource) + + def test_get_mine_global_map(self): + # assertion: with a global rule we get the "mycloud" class. + env_str = {'resource_registry': {"OS::*": "myCloud::*"}} + env = environment.Environment(env_str) + cls = resource.get_class('OS::ResourceType', 'fred', env) + self.assertEqual(cls, MyCloudResource) + + def test_get_mine_type_map(self): + # assertion: with a global rule we get the "mycloud" class. + env_str = {'resource_registry': { + "OS::ResourceType": "myCloud::ResourceType"}} + env = environment.Environment(env_str) + cls = resource.get_class('OS::ResourceType', 'fred', env) + self.assertEqual(cls, MyCloudResource) + + def test_get_mine_resource_map(self): + # assertion: with a global rule we get the "mycloud" class. + env_str = {'resource_registry': {'resources': {'fred': { + "OS::ResourceType": "myCloud::ResourceType"}}}} + env = environment.Environment(env_str) + cls = resource.get_class('OS::ResourceType', 'fred', env) + self.assertEqual(cls, MyCloudResource) + + def test_get_os_no_match(self): + # assertion: make sure 'fred' doesn't match 'jerry'. + env_str = {'resource_registry': {'resources': {'jerry': { + "OS::ResourceType": "myCloud::ResourceType"}}}} + env = environment.Environment(env_str) + cls = resource.get_class('OS::ResourceType', 'fred', env) + self.assertEqual(cls, generic_rsrc.GenericResource) + + def test_get_template_resource(self): + # assertion: if the name matches {.yaml|.template} we get the + # TemplateResource class. + env_str = {'resource_registry': {'resources': {'fred': { + "OS::ResourceType": "some_magic.yaml"}}}} + env = environment.Environment(env_str) + cls = resource.get_class('OS::ResourceType', 'fred', env) + self.assertEqual(cls, template_resource.TemplateResource)