From 87d0ca55090433efa6f41f36519fb2420b234fe4 Mon Sep 17 00:00:00 2001 From: Kanagaraj Manickam Date: Fri, 4 Nov 2016 11:36:48 +0530 Subject: [PATCH] Adds Keystone Domain resource plugin Change-Id: I50cf45efa3573031b767b05b5feb447871b603f0 implements: blueprint heat-keystone-domain-resource --- .../resources/openstack/keystone/domain.py | 97 +++++++++++++++ heat/tests/openstack/keystone/test_domain.py | 116 ++++++++++++++++++ 2 files changed, 213 insertions(+) create mode 100644 heat/engine/resources/openstack/keystone/domain.py create mode 100644 heat/tests/openstack/keystone/test_domain.py diff --git a/heat/engine/resources/openstack/keystone/domain.py b/heat/engine/resources/openstack/keystone/domain.py new file mode 100644 index 0000000000..a57ae1a2dd --- /dev/null +++ b/heat/engine/resources/openstack/keystone/domain.py @@ -0,0 +1,97 @@ +# +# 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.common.i18n import _ +from heat.engine import properties +from heat.engine import resource +from heat.engine import support + + +class KeystoneDomain(resource.Resource): + """Heat Template Resource for Keystone Domain. + + This plug-in helps to create, update and delete a keystone domain. Also + it can be used for enable or disable a given keystone domain. + """ + + support_status = support.SupportStatus( + version='8.0.0', + message=_('Supported versions: keystone v3')) + + default_client_name = 'keystone' + + entity = 'domains' + + PROPERTIES = ( + NAME, DESCRIPTION, ENABLED + ) = ( + 'name', 'description', 'enabled' + ) + + properties_schema = { + NAME: properties.Schema( + properties.Schema.STRING, + _('The name of the domain.'), + update_allowed=True + ), + DESCRIPTION: properties.Schema( + properties.Schema.STRING, + _('Description of keystone domain.'), + update_allowed=True + ), + ENABLED: properties.Schema( + properties.Schema.BOOLEAN, + _('This domain is enabled or disabled.'), + default=True, + update_allowed=True + ) + } + + def client(self): + return super(KeystoneDomain, self).client().client + + def handle_create(self): + name = (self.properties[self.NAME] or + self.physical_resource_name()) + description = self.properties[self.DESCRIPTION] + enabled = self.properties[self.ENABLED] + + domain = self.client().domains.create( + name=name, + description=description, + enabled=enabled) + + self.resource_id_set(domain.id) + + def handle_update(self, json_snippet, tmpl_diff, prop_diff): + if prop_diff: + description = prop_diff.get(self.DESCRIPTION) + enabled = prop_diff.get(self.ENABLED) + name = None + # Don't update the name if no change + if self.NAME in prop_diff: + name = prop_diff[self.NAME] or self.physical_resource_name() + + self.client().domains.update( + domain=self.resource_id, + name=name, + description=description, + enabled=enabled + ) + + +def resource_mapping(): + return { + 'OS::Keystone::Domain': KeystoneDomain + } diff --git a/heat/tests/openstack/keystone/test_domain.py b/heat/tests/openstack/keystone/test_domain.py new file mode 100644 index 0000000000..0c2d456e2e --- /dev/null +++ b/heat/tests/openstack/keystone/test_domain.py @@ -0,0 +1,116 @@ +# +# 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. + +import mock + +from heat.engine import resource +from heat.engine.resources.openstack.keystone import domain +from heat.engine import stack +from heat.engine import template +from heat.tests import common +from heat.tests import fakes +from heat.tests import utils + +KEYSTONE_REGION_TEMPLATE = { + 'heat_template_version': '2017-02-24', + 'resources': { + 'test_domain': { + 'type': 'OS::Keystone::Domain', + 'properties': { + 'name': 'test_domain_1', + 'description': 'Test domain', + 'enabled': 'True' + } + } + } +} + +RESOURCE_TYPE = 'OS::Keystone::Domain' + + +class KeystoneDomainTest(common.HeatTestCase): + def setUp(self): + super(KeystoneDomainTest, self).setUp() + + self.ctx = utils.dummy_context() + + self.stack = stack.Stack( + self.ctx, 'test_stack_keystone', + template.Template(KEYSTONE_REGION_TEMPLATE) + ) + + self.test_domain = self.stack['test_domain'] + + # Mock client + self.keystoneclient = mock.Mock() + self.patchobject(resource.Resource, 'client', + return_value=fakes.FakeKeystoneClient( + client=self.keystoneclient)) + self.domains = self.keystoneclient.domains + + keystone_client_plugin = mock.MagicMock() + self.test_domain.client_plugin = mock.MagicMock() + self.test_domain.client_plugin.return_value = keystone_client_plugin + + def _get_mock_domain(self): + value = mock.MagicMock() + domain_id = '477e8273-60a7-4c41-b683-fdb0bc7cd151' + value.id = domain_id + + return value + + def test_domain_handle_create(self): + mock_domain = self._get_mock_domain() + self.domains.create.return_value = mock_domain + + # validate the properties + self.assertEqual( + 'test_domain_1', + self.test_domain.properties.get(domain.KeystoneDomain.NAME)) + self.assertEqual( + 'Test domain', + self.test_domain.properties.get( + domain.KeystoneDomain.DESCRIPTION)) + self.assertEqual( + True, + self.test_domain.properties.get(domain.KeystoneDomain.ENABLED)) + + self.test_domain.handle_create() + + # validate domain creation + self.domains.create.assert_called_once_with( + name='test_domain_1', + description='Test domain', + enabled=True) + + # validate physical resource id + self.assertEqual(mock_domain.id, self.test_domain.resource_id) + + def test_domain_handle_update(self): + self.test_domain.resource_id = '477e8273-60a7-4c41-b683-fdb0bc7cd151' + + prop_diff = {domain.KeystoneDomain.DESCRIPTION: + 'Test Domain updated', + domain.KeystoneDomain.ENABLED: False, + domain.KeystoneDomain.NAME: 'test_domain_2'} + + self.test_domain.handle_update(json_snippet=None, + tmpl_diff=None, + prop_diff=prop_diff) + + self.domains.update.assert_called_once_with( + domain=self.test_domain.resource_id, + description=prop_diff[domain.KeystoneDomain.DESCRIPTION], + enabled=prop_diff[domain.KeystoneDomain.ENABLED], + name='test_domain_2' + )