From a899536a4dbc8b9e1cd06d3295a86dbebc751aa4 Mon Sep 17 00:00:00 2001 From: Kanagaraj Manickam Date: Thu, 5 Nov 2015 17:18:01 +0530 Subject: [PATCH] Adds OS::Keystone::Region resource plug-in implments blueprint: heat-keystone-region-resource Change-Id: Icaefe55929a1d36614ecdd042e791c1315ecf0ad --- .../resources/openstack/keystone/region.py | 105 ++++++++++++ heat/tests/openstack/keystone/test_region.py | 150 ++++++++++++++++++ 2 files changed, 255 insertions(+) create mode 100644 heat/engine/resources/openstack/keystone/region.py create mode 100644 heat/tests/openstack/keystone/test_region.py diff --git a/heat/engine/resources/openstack/keystone/region.py b/heat/engine/resources/openstack/keystone/region.py new file mode 100644 index 0000000000..7dad0d33d5 --- /dev/null +++ b/heat/engine/resources/openstack/keystone/region.py @@ -0,0 +1,105 @@ +# +# 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 six.moves.urllib import parse + +from heat.common.i18n import _ +from heat.engine import constraints +from heat.engine import properties +from heat.engine import resource +from heat.engine import support + + +class KeystoneRegion(resource.Resource): + """Heat Template Resource for Keystone Region. + + This plug-in helps to create, update and delete a keystone region. Also + it can be used for enable or disable a given keystone region. + """ + + support_status = support.SupportStatus( + version='6.0.0', + message=_('Supported versions: keystone v3')) + + default_client_name = 'keystone' + + entity = 'regions' + + PROPERTIES = ( + ID, PARENT_REGION, DESCRIPTION, ENABLED + ) = ( + 'id', 'parent_region', 'description', 'enabled' + ) + + properties_schema = { + ID: properties.Schema( + properties.Schema.STRING, + _('The user-defined region ID and should unique to the OpenStack ' + 'deployment. While creating the region, heat will url encode ' + 'this ID.') + ), + PARENT_REGION: properties.Schema( + properties.Schema.STRING, + _('If the region is hierarchically a child of another region, ' + 'set this parameter to the ID of the parent region.'), + update_allowed=True, + constraints=[constraints.CustomConstraint('keystone.region')] + ), + DESCRIPTION: properties.Schema( + properties.Schema.STRING, + _('Description of keystone region.'), + update_allowed=True + ), + ENABLED: properties.Schema( + properties.Schema.BOOLEAN, + _('This region is enabled or disabled.'), + default=True, + update_allowed=True + ) + } + + def client(self): + return super(KeystoneRegion, self).client().client + + def handle_create(self): + region_id = self.properties[self.ID] + description = self.properties[self.DESCRIPTION] + parent_region = self.properties[self.PARENT_REGION] + enabled = self.properties[self.ENABLED] + + region = self.client().regions.create( + id=parse.quote(region_id) if region_id else None, + parent_region=parent_region, + description=description, + enabled=enabled) + + self.resource_id_set(region.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) + parent_region = prop_diff.get(self.PARENT_REGION) + + self.client().regions.update( + region=self.resource_id, + parent_region=parent_region, + description=description, + enabled=enabled + ) + + +def resource_mapping(): + return { + 'OS::Keystone::Region': KeystoneRegion + } diff --git a/heat/tests/openstack/keystone/test_region.py b/heat/tests/openstack/keystone/test_region.py new file mode 100644 index 0000000000..d2db7b79a4 --- /dev/null +++ b/heat/tests/openstack/keystone/test_region.py @@ -0,0 +1,150 @@ +# +# 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 six.moves.urllib import parse + +from heat.engine.resources.openstack.keystone import region +from heat.engine import stack +from heat.engine import template +from heat.tests import common +from heat.tests import utils + +KEYSTONE_REGION_TEMPLATE = { + 'heat_template_version': '2015-10-15', + 'resources': { + 'test_region': { + 'type': 'OS::Keystone::Region', + 'properties': { + 'id': 'test_region_1', + 'description': 'Test region', + 'parent_region': 'default_region', + 'enabled': 'True' + } + } + } +} + +RESOURCE_TYPE = 'OS::Keystone::Region' + + +class KeystoneRegionTest(common.HeatTestCase): + def setUp(self): + super(KeystoneRegionTest, self).setUp() + + self.ctx = utils.dummy_context() + + self.stack = stack.Stack( + self.ctx, 'test_stack_keystone', + template.Template(KEYSTONE_REGION_TEMPLATE) + ) + + self.test_region = self.stack['test_region'] + + # Mock client + self.keystoneclient = mock.MagicMock() + self.test_region.client = mock.MagicMock() + self.test_region.client.return_value = self.keystoneclient + self.regions = self.keystoneclient.regions + + keystone_client_plugin = mock.MagicMock() + self.test_region.client_plugin = mock.MagicMock() + self.test_region.client_plugin.return_value = keystone_client_plugin + + def _get_mock_region(self): + value = mock.MagicMock() + region_id = '477e8273-60a7-4c41-b683-fdb0bc7cd151' + value.id = region_id + + return value + + def test_resource_mapping(self): + mapping = region.resource_mapping() + self.assertEqual(1, len(mapping)) + self.assertEqual(region.KeystoneRegion, mapping[RESOURCE_TYPE]) + self.assertIsInstance(self.test_region, region.KeystoneRegion) + + def test_region_handle_create(self): + mock_region = self._get_mock_region() + self.regions.create.return_value = mock_region + + # validate the properties + self.assertEqual( + 'test_region_1', + self.test_region.properties.get(region.KeystoneRegion.ID)) + self.assertEqual( + 'Test region', + self.test_region.properties.get( + region.KeystoneRegion.DESCRIPTION)) + self.assertEqual( + 'default_region', + self.test_region.properties.get( + region.KeystoneRegion.PARENT_REGION)) + self.assertEqual( + True, + self.test_region.properties.get(region.KeystoneRegion.ENABLED)) + + self.test_region.handle_create() + + # validate region creation + self.regions.create.assert_called_once_with( + id=parse.quote('test_region_1'), + description='Test region', + parent_region='default_region', + enabled=True) + + # validate physical resource id + self.assertEqual(mock_region.id, self.test_region.resource_id) + + def test_region_handle_create_minimal(self): + values = { + 'description': 'sample region', + 'enabled': True, + 'parent_region': None, + 'id': None + } + + def _side_effect(key): + return values[key] + + mock_region = self._get_mock_region() + self.regions.create.return_value = mock_region + self.test_region.properties = mock.MagicMock() + self.test_region.properties.__getitem__.side_effect = _side_effect + + self.test_region.handle_create() + + self.regions.create.assert_called_once_with( + id=None, + description='sample region', + parent_region=None, + enabled=True) + + def test_region_handle_update(self): + self.test_region.resource_id = '477e8273-60a7-4c41-b683-fdb0bc7cd151' + + prop_diff = {region.KeystoneRegion.DESCRIPTION: + 'Test Region updated', + region.KeystoneRegion.ENABLED: False, + region.KeystoneRegion.PARENT_REGION: 'test_parent_region'} + + self.test_region.handle_update(json_snippet=None, + tmpl_diff=None, + prop_diff=prop_diff) + + self.regions.update.assert_called_once_with( + region=self.test_region.resource_id, + description=prop_diff[region.KeystoneRegion.DESCRIPTION], + enabled=prop_diff[region.KeystoneRegion.ENABLED], + parent_region='test_parent_region' + )