diff --git a/shade/__init__.py b/shade/__init__.py index 0df2a171e..05719b861 100644 --- a/shade/__init__.py +++ b/shade/__init__.py @@ -4744,3 +4744,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 ef20c2199..dcbd0571b 100644 --- a/shade/_tasks.py +++ b/shade/_tasks.py @@ -589,3 +589,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)