diff options
Diffstat (limited to 'aria/multivim-plugin/keystone_plugin')
-rw-r--r-- | aria/multivim-plugin/keystone_plugin/__init__.py | 14 | ||||
-rw-r--r-- | aria/multivim-plugin/keystone_plugin/project.py | 150 | ||||
-rw-r--r-- | aria/multivim-plugin/keystone_plugin/tests/__init__.py | 0 | ||||
-rw-r--r-- | aria/multivim-plugin/keystone_plugin/tests/test.py | 115 |
4 files changed, 279 insertions, 0 deletions
diff --git a/aria/multivim-plugin/keystone_plugin/__init__.py b/aria/multivim-plugin/keystone_plugin/__init__.py new file mode 100644 index 0000000000..809f033a55 --- /dev/null +++ b/aria/multivim-plugin/keystone_plugin/__init__.py @@ -0,0 +1,14 @@ +######### +# Copyright (c) 2015 GigaSpaces Technologies Ltd. All rights reserved +# +# 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. diff --git a/aria/multivim-plugin/keystone_plugin/project.py b/aria/multivim-plugin/keystone_plugin/project.py new file mode 100644 index 0000000000..223ffbbb5c --- /dev/null +++ b/aria/multivim-plugin/keystone_plugin/project.py @@ -0,0 +1,150 @@ +######### +# Copyright (c) 2015 GigaSpaces Technologies Ltd. All rights reserved +# +# 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 cloudify import ctx +from cloudify.decorators import operation +from cloudify.exceptions import NonRecoverableError + +from openstack_plugin_common import (with_keystone_client, + with_nova_client, + with_cinder_client, + with_neutron_client, + get_resource_id, + use_external_resource, + delete_resource_and_runtime_properties, + validate_resource, + COMMON_RUNTIME_PROPERTIES_KEYS, + OPENSTACK_ID_PROPERTY, + OPENSTACK_TYPE_PROPERTY, + OPENSTACK_NAME_PROPERTY) + + +PROJECT_OPENSTACK_TYPE = 'project' + +TENANT_QUOTA_TYPE = 'quota' + +RUNTIME_PROPERTIES_KEYS = COMMON_RUNTIME_PROPERTIES_KEYS + + +@operation +@with_keystone_client +def create(keystone_client, **kwargs): + if use_external_resource(ctx, keystone_client, PROJECT_OPENSTACK_TYPE): + return + + project_dict = { + 'name': get_resource_id(ctx, PROJECT_OPENSTACK_TYPE), + 'domain': 'default' + } + + project_dict.update(ctx.node.properties['project']) + project = keystone_client.projects.create(**project_dict) + + ctx.instance.runtime_properties[OPENSTACK_ID_PROPERTY] = project.id + ctx.instance.runtime_properties[OPENSTACK_TYPE_PROPERTY] = \ + PROJECT_OPENSTACK_TYPE + ctx.instance.runtime_properties[OPENSTACK_NAME_PROPERTY] = project.name + + +@operation +@with_keystone_client +@with_nova_client +@with_cinder_client +@with_neutron_client +def start(keystone_client, nova_client, cinder_client, neutron_client, + **kwargs): + project_id = ctx.instance.runtime_properties[OPENSTACK_ID_PROPERTY] + users = ctx.node.properties['users'] + validate_users(users, keystone_client) + + assign_users(project_id, users, keystone_client) + + quota = ctx.node.properties[TENANT_QUOTA_TYPE] + update_quota(project_id, quota, nova_client, 'nova') + update_quota(project_id, quota, neutron_client, 'neutron') + update_quota(project_id, quota, cinder_client, 'cinder') + + +@operation +@with_keystone_client +@with_nova_client +@with_cinder_client +@with_neutron_client +def delete(keystone_client, nova_client, cinder_client, + neutron_client, **kwargs): + tenant_id = ctx.instance.runtime_properties[OPENSTACK_ID_PROPERTY] + quota = ctx.node.properties[TENANT_QUOTA_TYPE] + delete_quota(tenant_id, quota, nova_client, 'nova') + delete_quota(tenant_id, quota, neutron_client, 'neutron') + delete_quota(tenant_id, quota, cinder_client, 'cinder') + delete_resource_and_runtime_properties(ctx, keystone_client, + RUNTIME_PROPERTIES_KEYS) + + +@operation +@with_keystone_client +def creation_validation(keystone_client, **kwargs): + validate_resource(ctx, keystone_client, PROJECT_OPENSTACK_TYPE) + + +def assign_users(project_id, users, keystone_client): + for user in users: + roles = user['roles'] + u = keystone_client.users.find(name=user['name']) + for role in roles: + r = keystone_client.roles.find(name=role) + keystone_client.roles.grant(user=u.id, + project=project_id, + role=r.id) + + +def validate_users(users, keystone_client): + user_names = [user['name'] for user in users] + if len(user_names) > len(set(user_names)): + raise NonRecoverableError('Users are not unique') + + for user_name in user_names: + keystone_client.users.find(name=user_name) + + for user in users: + if len(user['roles']) > len(set(user['roles'])): + msg = 'Roles for user {} are not unique' + raise NonRecoverableError(msg.format(user['name'])) + + role_names = {role for user in users for role in user['roles']} + for role_name in role_names: + keystone_client.roles.find(name=role_name) + + +def update_quota(tenant_id, quota, client, what_quota): + updated_quota = quota.get(what_quota) + if updated_quota: + if what_quota == 'neutron': + new_quota = client.update_quota(tenant_id=tenant_id, + body={'quota': updated_quota}) + else: + new_quota = client.quotas.update(tenant_id=tenant_id, + **updated_quota) + ctx.logger.info( + 'Updated {0} quota: {1}'.format(what_quota, str(new_quota))) + + +def delete_quota(project_id, quota, client, what_quota): + deleting_quota = quota.get(what_quota) + if deleting_quota: + if what_quota == 'neutron': + client.delete_quota(tenant_id=project_id) + else: + client.quotas.delete(tenant_id=project_id) diff --git a/aria/multivim-plugin/keystone_plugin/tests/__init__.py b/aria/multivim-plugin/keystone_plugin/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/aria/multivim-plugin/keystone_plugin/tests/__init__.py diff --git a/aria/multivim-plugin/keystone_plugin/tests/test.py b/aria/multivim-plugin/keystone_plugin/tests/test.py new file mode 100644 index 0000000000..de6567ba3a --- /dev/null +++ b/aria/multivim-plugin/keystone_plugin/tests/test.py @@ -0,0 +1,115 @@ +import mock +import unittest + +from cloudify.context import NODE_INSTANCE + +from cloudify.mocks import ( + MockContext, + MockNodeInstanceContext, + MockNodeContext +) +from openstack_plugin_common import ( + OPENSTACK_ID_PROPERTY, + OPENSTACK_NAME_PROPERTY, + OPENSTACK_TYPE_PROPERTY + ) +from keystone_plugin.project import PROJECT_OPENSTACK_TYPE +import keystone_plugin + + +class TestProject(unittest.TestCase): + + test_id = 'test-id' + test_name = 'test-name' + test_deployment_id = 'test-deployment-id' + test_user = 'test-user' + test_role = 'test-role' + + class MockProjectOS: + def __init__(self, id, name): + self._id = id + self._name = name + self._users = {} + + @property + def id(self): + return self._id + + @property + def name(self): + return self._name + + def find(self, *_, **__): + return mock.MagicMock(id='test-role') + + def grant(self, role, user, *_, **__): + self._users[user] = role + + def mock_keystone_client(self, mock_project): + keystone_client = mock.MagicMock() + keystone_client.projects.create.return_value = mock_project + keystone_client.users.find.return_value = mock.MagicMock( + id=self.test_user) + keystone_client.roles = mock_project + return keystone_client + + def mock_ctx(self, test_vars, test_id, + test_deployment_id, runtime_properties=None): + ctx = MockContext() + ctx.node = MockNodeContext(properties=test_vars) + ctx.instance = MockNodeInstanceContext( + id=test_id, runtime_properties=runtime_properties or {}) + ctx.deployment = mock.Mock() + ctx.deployment.id = test_deployment_id + ctx.type = NODE_INSTANCE + return ctx + + @mock.patch('openstack_plugin_common._put_client_in_kw', + autospec=True, return_value=None) + def test_keystone_project_create(self, *_): + test_vars = { + 'project': {}, + 'resource_id': '', + 'quota': {}, + 'users': {} + } + + ctx = self.mock_ctx(test_vars, self.test_id, self.test_deployment_id) + keystone_plugin.project.ctx = ctx + keystone_plugin.project.create( + self.mock_keystone_client(self.MockProjectOS(self.test_id, + self.test_name))) + self.assertEqual(self.test_name, + ctx.instance.runtime_properties[ + OPENSTACK_NAME_PROPERTY]) + self.assertEqual(self.test_id, + ctx.instance.runtime_properties[ + OPENSTACK_ID_PROPERTY]) + self.assertEqual(PROJECT_OPENSTACK_TYPE, + ctx.instance.runtime_properties[ + OPENSTACK_TYPE_PROPERTY]) + + @mock.patch('openstack_plugin_common._put_client_in_kw', + autospec=True, return_value=None) + def test_assign_user(self, *_): + test_vars = { + 'project': {}, + 'resource_id': '', + 'quota': {}, + 'users': [{'name': self.test_user, + 'roles': [self.test_role]}] + } + ctx = self.mock_ctx(test_vars, + self.test_id, + self.test_deployment_id, + {OPENSTACK_ID_PROPERTY: self.test_id}) + mock_project = self.MockProjectOS(self.test_id, self.test_name) + keystone_client = self.mock_keystone_client(mock_project) + keystone_plugin.project.ctx = ctx + keystone_plugin.project.start( + keystone_client, + mock.MagicMock(), # nova_client + mock.MagicMock(), # cinder_client + mock.MagicMock()) # neutron_client + self.assertEqual({self.test_user: self.test_role}, + mock_project._users) |