From baa36a8abb79d2c3a52d2cb7082c81f9af7fa109 Mon Sep 17 00:00:00 2001 From: David Shrewsbury Date: Tue, 6 Oct 2015 22:14:54 -0400 Subject: [PATCH] Add create/delete for keystone roles This completes the replacement of: Ifd74cbcef9dd0f3531912995da8ad7b75dd19d44 Add create and delete methods for roles. Note that there is no update method (the review we are replacing had an update method). This is because keystone v2 does NOT have support for this, only v3. Change-Id: Ib037484c38673f10bd2b2fe2df5ca87056d06e6d --- shade/__init__.py | 42 +++++++++++++++++++++++++ shade/_tasks.py | 10 ++++++ shade/tests/functional/test_identity.py | 34 ++++++++++++++++++++ shade/tests/unit/test_identity_roles.py | 23 ++++++++++++++ 4 files changed, 109 insertions(+) diff --git a/shade/__init__.py b/shade/__init__.py index 29f08df77..180c0233d 100644 --- a/shade/__init__.py +++ b/shade/__init__.py @@ -4611,3 +4611,45 @@ class OperatorCloud(OpenStackCloud): :raises: OpenStackCloudException on operation error. """ self._mod_flavor_access('remove', flavor_id, project_id) + + def create_role(self, name): + """Create a Keystone role. + + :param string name: The name of the role. + + :returns: a dict containing the role description + + :raise OpenStackCloudException: if the role cannot be created + """ + try: + role = self.manager.submitTask( + _tasks.RoleCreate(name=name) + ) + except Exception as e: + raise OpenStackCloudException(str(e)) + return meta.obj_to_dict(role) + + def delete_role(self, name_or_id): + """Delete a Keystone role. + + :param string id: Name or id of the role to delete. + + :returns: True if delete succeeded, False otherwise. + + :raises: ``OpenStackCloudException`` if something goes wrong during + the openstack API call. + """ + role = self.get_role(name_or_id) + if role is None: + self.log.debug( + "Role {0} not found for deleting".format(name_or_id)) + return False + + try: + self.manager.submitTask(_tasks.RoleDelete(role=role['id'])) + except Exception as e: + raise OpenStackCloudException( + "Unable to delete role {0}: {1}".format(name_or_id, e) + ) + + return True diff --git a/shade/_tasks.py b/shade/_tasks.py index 8a653eccd..c250accd8 100644 --- a/shade/_tasks.py +++ b/shade/_tasks.py @@ -579,3 +579,13 @@ class RecordGet(task_manager.Task): class RoleList(task_manager.Task): def main(self, client): return client.keystone_client.roles.list() + + +class RoleCreate(task_manager.Task): + def main(self, client): + return client.keystone_client.roles.create(**self.args) + + +class RoleDelete(task_manager.Task): + def main(self, client): + return client.keystone_client.roles.delete(**self.args) diff --git a/shade/tests/functional/test_identity.py b/shade/tests/functional/test_identity.py index adeacc23d..b3b31b272 100644 --- a/shade/tests/functional/test_identity.py +++ b/shade/tests/functional/test_identity.py @@ -19,7 +19,11 @@ test_identity Functional tests for `shade` identity methods. """ +import random +import string + from shade import operator_cloud +from shade import OpenStackCloudException from shade.tests import base @@ -27,6 +31,22 @@ class TestIdentity(base.TestCase): def setUp(self): super(TestIdentity, self).setUp() self.cloud = operator_cloud(cloud='devstack-admin') + self.role_prefix = 'test_role' + ''.join( + random.choice(string.ascii_lowercase) for _ in range(5)) + self.addCleanup(self._cleanup_roles) + + def _cleanup_roles(self): + exception_list = list() + for role in self.cloud.list_roles(): + if role['name'].startswith(self.role_prefix): + try: + self.cloud.delete_role(role['name']) + except Exception as e: + exception_list.append(str(e)) + continue + + if exception_list: + raise OpenStackCloudException('\n'.join(exception_list)) def test_list_roles(self): roles = self.cloud.list_roles() @@ -45,3 +65,17 @@ class TestIdentity(base.TestCase): self.assertIsNotNone(roles) self.assertEqual(1, len(roles)) self.assertEqual('admin', roles[0]['name']) + + def test_create_role(self): + role_name = self.role_prefix + '_create_role' + role = self.cloud.create_role(role_name) + self.assertIsNotNone(role) + self.assertIn('id', role) + self.assertIn('name', role) + self.assertEqual(role_name, role['name']) + + def test_delete_role(self): + role_name = self.role_prefix + '_delete_role' + role = self.cloud.create_role(role_name) + self.assertIsNotNone(role) + self.assertTrue(self.cloud.delete_role(role_name)) diff --git a/shade/tests/unit/test_identity_roles.py b/shade/tests/unit/test_identity_roles.py index d3ab3309f..4490d6e51 100644 --- a/shade/tests/unit/test_identity_roles.py +++ b/shade/tests/unit/test_identity_roles.py @@ -14,6 +14,7 @@ import mock import shade +from shade import meta from shade.tests.unit import base from shade.tests import fakes @@ -40,3 +41,25 @@ class TestIdentityRoles(base.TestCase): self.assertIsNotNone(role) self.assertEqual('1234', role['id']) self.assertEqual('fake_role', role['name']) + + @mock.patch.object(shade.OpenStackCloud, 'keystone_client') + def test_create_role(self, mock_keystone): + role_name = 'tootsie_roll' + role_obj = fakes.FakeRole(id='1234', name=role_name) + mock_keystone.roles.create.return_value = role_obj + + role = self.cloud.create_role(role_name) + + mock_keystone.roles.create.assert_called_once_with( + name=role_name + ) + self.assertIsNotNone(role) + self.assertEqual(role_name, role['name']) + + @mock.patch.object(shade.OperatorCloud, 'get_role') + @mock.patch.object(shade.OpenStackCloud, 'keystone_client') + def test_delete_role(self, mock_keystone, mock_get): + role_obj = fakes.FakeRole(id='1234', name='aaa') + mock_get.return_value = meta.obj_to_dict(role_obj) + self.assertTrue(self.cloud.delete_role('1234')) + self.assertTrue(mock_keystone.roles.delete.called)