summaryrefslogtreecommitdiffstats
path: root/aria/multivim-plugin/keystone_plugin
diff options
context:
space:
mode:
Diffstat (limited to 'aria/multivim-plugin/keystone_plugin')
-rw-r--r--aria/multivim-plugin/keystone_plugin/__init__.py14
-rw-r--r--aria/multivim-plugin/keystone_plugin/project.py150
-rw-r--r--aria/multivim-plugin/keystone_plugin/tests/__init__.py0
-rw-r--r--aria/multivim-plugin/keystone_plugin/tests/test.py115
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)