Merge "Add role assignments list support to identity v3"
This commit is contained in:
		
							
								
								
									
										156
									
								
								openstackclient/identity/v3/role_assignment.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								openstackclient/identity/v3/role_assignment.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,156 @@ | ||||
| #   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. | ||||
| # | ||||
|  | ||||
| """Identity v3 Assignment action implementations """ | ||||
|  | ||||
| import logging | ||||
|  | ||||
| from cliff import lister | ||||
|  | ||||
| from openstackclient.common import utils | ||||
|  | ||||
|  | ||||
| class ListRoleAssignment(lister.Lister): | ||||
|     """Lists role assignments according to the given filters""" | ||||
|  | ||||
|     log = logging.getLogger(__name__ + '.ListRoleAssignment') | ||||
|  | ||||
|     def get_parser(self, prog_name): | ||||
|         parser = super(ListRoleAssignment, self).get_parser(prog_name) | ||||
|         parser.add_argument( | ||||
|             '--effective', | ||||
|             action="store_true", | ||||
|             default=False, | ||||
|             help='Returns only effective role assignments', | ||||
|         ) | ||||
|         parser.add_argument( | ||||
|             '--role', | ||||
|             metavar='<role>', | ||||
|             help='Name or ID of role to filter', | ||||
|         ) | ||||
|         user_or_group = parser.add_mutually_exclusive_group() | ||||
|         user_or_group.add_argument( | ||||
|             '--user', | ||||
|             metavar='<user>', | ||||
|             help='Name or ID of user to filter', | ||||
|         ) | ||||
|         user_or_group.add_argument( | ||||
|             '--group', | ||||
|             metavar='<group>', | ||||
|             help='Name or ID of group to filter', | ||||
|         ) | ||||
|         domain_or_project = parser.add_mutually_exclusive_group() | ||||
|         domain_or_project.add_argument( | ||||
|             '--domain', | ||||
|             metavar='<domain>', | ||||
|             help='Name or ID of domain to filter', | ||||
|         ) | ||||
|         domain_or_project.add_argument( | ||||
|             '--project', | ||||
|             metavar='<project>', | ||||
|             help='Name or ID of project to filter', | ||||
|         ) | ||||
|  | ||||
|         return parser | ||||
|  | ||||
|     def _as_tuple(self, assignment): | ||||
|         return (assignment.role, assignment.user, assignment.group, | ||||
|                 assignment.project, assignment.domain) | ||||
|  | ||||
|     def take_action(self, parsed_args): | ||||
|         self.log.debug('take_action(%s)' % parsed_args) | ||||
|         identity_client = self.app.client_manager.identity | ||||
|  | ||||
|         role = None | ||||
|         if parsed_args.role: | ||||
|             role = utils.find_resource( | ||||
|                 identity_client.roles, | ||||
|                 parsed_args.role, | ||||
|             ) | ||||
|  | ||||
|         user = None | ||||
|         if parsed_args.user: | ||||
|             user = utils.find_resource( | ||||
|                 identity_client.users, | ||||
|                 parsed_args.user, | ||||
|             ) | ||||
|  | ||||
|         domain = None | ||||
|         if parsed_args.domain: | ||||
|             domain = utils.find_resource( | ||||
|                 identity_client.domains, | ||||
|                 parsed_args.domain, | ||||
|             ) | ||||
|  | ||||
|         project = None | ||||
|         if parsed_args.project: | ||||
|             project = utils.find_resource( | ||||
|                 identity_client.projects, | ||||
|                 parsed_args.project, | ||||
|             ) | ||||
|  | ||||
|         group = None | ||||
|         if parsed_args.group: | ||||
|             group = utils.find_resource( | ||||
|                 identity_client.groups, | ||||
|                 parsed_args.group, | ||||
|             ) | ||||
|  | ||||
|         effective = True if parsed_args.effective else False | ||||
|         self.log.debug('take_action(%s)' % parsed_args) | ||||
|         columns = ('Role', 'User', 'Group', 'Project', 'Domain') | ||||
|         data = identity_client.role_assignments.list( | ||||
|             domain=domain, | ||||
|             user=user, | ||||
|             group=group, | ||||
|             project=project, | ||||
|             role=role, | ||||
|             effective=effective) | ||||
|  | ||||
|         data_parsed = [] | ||||
|         for assignment in data: | ||||
|             # Removing the extra "scope" layer in the assignment json | ||||
|             scope = assignment.scope | ||||
|             if 'project' in scope: | ||||
|                 setattr(assignment, 'project', scope['project']['id']) | ||||
|                 assignment.domain = '' | ||||
|             elif 'domain' in scope: | ||||
|                 setattr(assignment, 'domain', scope['domain']['id']) | ||||
|                 assignment.project = '' | ||||
|  | ||||
|             else: | ||||
|                 assignment.domain = '' | ||||
|                 assignment.project = '' | ||||
|  | ||||
|             del assignment.scope | ||||
|  | ||||
|             if hasattr(assignment, 'user'): | ||||
|                 setattr(assignment, 'user', assignment.user['id']) | ||||
|                 assignment.group = '' | ||||
|             elif hasattr(assignment, 'group'): | ||||
|                 setattr(assignment, 'group', assignment.group['id']) | ||||
|                 assignment.user = '' | ||||
|             else: | ||||
|                 assignment.user = '' | ||||
|                 assignment.group = '' | ||||
|  | ||||
|             if hasattr(assignment, 'role'): | ||||
|                 setattr(assignment, 'role', assignment.role['id']) | ||||
|             else: | ||||
|                 assignment.role = '' | ||||
|  | ||||
|             # Creating a tuple from data object fields | ||||
|             # (including the blank ones) | ||||
|             data_parsed.append(self._as_tuple(assignment)) | ||||
|  | ||||
|         return columns, tuple(data_parsed) | ||||
| @@ -114,6 +114,31 @@ IDENTITY_PROVIDER = { | ||||
|     'description': idp_description | ||||
| } | ||||
|  | ||||
| #Assignments | ||||
| ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID = { | ||||
|     'scope': {'project': {'id': project_id}}, | ||||
|     'user': {'id': user_id}, | ||||
|     'role': {'id': role_id}, | ||||
| } | ||||
|  | ||||
| ASSIGNMENT_WITH_PROJECT_ID_AND_GROUP_ID = { | ||||
|     'scope': {'project': {'id': project_id}}, | ||||
|     'group': {'id': group_id}, | ||||
|     'role': {'id': role_id}, | ||||
| } | ||||
|  | ||||
| ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID = { | ||||
|     'scope': {'domain': {'id': domain_id}}, | ||||
|     'user': {'id': user_id}, | ||||
|     'role': {'id': role_id}, | ||||
| } | ||||
|  | ||||
| ASSIGNMENT_WITH_DOMAIN_ID_AND_GROUP_ID = { | ||||
|     'scope': {'domain': {'id': domain_id}}, | ||||
|     'group': {'id': group_id}, | ||||
|     'role': {'id': role_id}, | ||||
| } | ||||
|  | ||||
|  | ||||
| class FakeIdentityv3Client(object): | ||||
|     def __init__(self, **kwargs): | ||||
| @@ -130,6 +155,8 @@ class FakeIdentityv3Client(object): | ||||
|         self.service_catalog = mock.Mock() | ||||
|         self.users = mock.Mock() | ||||
|         self.users.resource_class = fakes.FakeResource(None, {}) | ||||
|         self.role_assignments = mock.Mock() | ||||
|         self.role_assignments.resource_class = fakes.FakeResource(None, {}) | ||||
|         self.auth_token = kwargs['token'] | ||||
|         self.management_url = kwargs['endpoint'] | ||||
|  | ||||
|   | ||||
							
								
								
									
										388
									
								
								openstackclient/tests/identity/v3/test_role_assignment.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										388
									
								
								openstackclient/tests/identity/v3/test_role_assignment.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,388 @@ | ||||
| #   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 copy | ||||
|  | ||||
| from openstackclient.identity.v3 import role_assignment | ||||
| from openstackclient.tests import fakes | ||||
| from openstackclient.tests.identity.v3 import fakes as identity_fakes | ||||
|  | ||||
|  | ||||
| class TestRoleAssignment(identity_fakes.TestIdentityv3): | ||||
|  | ||||
|     def setUp(self): | ||||
|         super(TestRoleAssignment, self).setUp() | ||||
|  | ||||
|  | ||||
| class TestRoleAssignmentList(TestRoleAssignment): | ||||
|  | ||||
|     def setUp(self): | ||||
|         super(TestRoleAssignment, self).setUp() | ||||
|  | ||||
|         # Get a shortcut to the UserManager Mock | ||||
|         self.users_mock = self.app.client_manager.identity.users | ||||
|         self.users_mock.reset_mock() | ||||
|  | ||||
|         # Get a shortcut to the GroupManager Mock | ||||
|         self.groups_mock = self.app.client_manager.identity.groups | ||||
|         self.groups_mock.reset_mock() | ||||
|  | ||||
|         # Get a shortcut to the DomainManager Mock | ||||
|         self.domains_mock = self.app.client_manager.identity.domains | ||||
|         self.domains_mock.reset_mock() | ||||
|  | ||||
|         # Get a shortcut to the ProjectManager Mock | ||||
|         self.projects_mock = self.app.client_manager.identity.projects | ||||
|         self.projects_mock.reset_mock() | ||||
|  | ||||
|         # Get a shortcut to the RoleManager Mock | ||||
|         self.roles_mock = self.app.client_manager.identity.roles | ||||
|         self.roles_mock.reset_mock() | ||||
|  | ||||
|         self.role_assignments_mock = self.app.client_manager.identity.\ | ||||
|             role_assignments | ||||
|         self.role_assignments_mock.reset_mock() | ||||
|  | ||||
|         # Get the command object to test | ||||
|         self.cmd = role_assignment.ListRoleAssignment(self.app, None) | ||||
|  | ||||
|     def test_role_assignment_list_no_filters(self): | ||||
|  | ||||
|         self.role_assignments_mock.list.return_value = [ | ||||
|             fakes.FakeResource( | ||||
|                 None, | ||||
|                 copy.deepcopy( | ||||
|                     identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID), | ||||
|                 loaded=True, | ||||
|             ), | ||||
|             fakes.FakeResource( | ||||
|                 None, | ||||
|                 copy.deepcopy( | ||||
|                     identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_GROUP_ID), | ||||
|                 loaded=True, | ||||
|             ), | ||||
|         ] | ||||
|  | ||||
|         arglist = [] | ||||
|         verifylist = [] | ||||
|         parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||||
|  | ||||
|         # DisplayCommandBase.take_action() returns two tuples | ||||
|         columns, data = self.cmd.take_action(parsed_args) | ||||
|  | ||||
|         self.role_assignments_mock.list.assert_called_with( | ||||
|             domain=None, | ||||
|             group=None, | ||||
|             effective=False, | ||||
|             role=None, | ||||
|             user=None, | ||||
|             project=None) | ||||
|  | ||||
|         collist = ('Role', 'User', 'Group', 'Project', 'Domain') | ||||
|         self.assertEqual(columns, collist) | ||||
|         datalist = (( | ||||
|             identity_fakes.role_id, | ||||
|             identity_fakes.user_id, | ||||
|             '', | ||||
|             identity_fakes.project_id, | ||||
|             '' | ||||
|         ), (identity_fakes.role_id, | ||||
|             '', | ||||
|             identity_fakes.group_id, | ||||
|             identity_fakes.project_id, | ||||
|             '' | ||||
|             ),) | ||||
|         self.assertEqual(tuple(data), datalist) | ||||
|  | ||||
|     def test_role_assignment_list_user(self): | ||||
|  | ||||
|         self.role_assignments_mock.list.return_value = [ | ||||
|             fakes.FakeResource( | ||||
|                 None, | ||||
|                 copy.deepcopy( | ||||
|                     identity_fakes.ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID), | ||||
|                 loaded=True, | ||||
|             ), | ||||
|             fakes.FakeResource( | ||||
|                 None, | ||||
|                 copy.deepcopy( | ||||
|                     identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID), | ||||
|                 loaded=True, | ||||
|             ), | ||||
|         ] | ||||
|  | ||||
|         arglist = [ | ||||
|             '--user', identity_fakes.user_name | ||||
|         ] | ||||
|         verifylist = [ | ||||
|             ('user', identity_fakes.user_name), | ||||
|             ('group', None), | ||||
|             ('domain', None), | ||||
|             ('project', None), | ||||
|             ('role', None), | ||||
|             ('effective', False), | ||||
|         ] | ||||
|         parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||||
|  | ||||
|         # DisplayCommandBase.take_action() returns two tuples | ||||
|         columns, data = self.cmd.take_action(parsed_args) | ||||
|  | ||||
|         self.role_assignments_mock.list.assert_called_with( | ||||
|             domain=None, | ||||
|             user=self.users_mock.get(), | ||||
|             group=None, | ||||
|             project=None, | ||||
|             role=None, | ||||
|             effective=False) | ||||
|  | ||||
|         collist = ('Role', 'User', 'Group', 'Project', 'Domain') | ||||
|         self.assertEqual(columns, collist) | ||||
|         datalist = (( | ||||
|             identity_fakes.role_id, | ||||
|             identity_fakes.user_id, | ||||
|             '', | ||||
|             '', | ||||
|             identity_fakes.domain_id | ||||
|         ), (identity_fakes.role_id, | ||||
|             identity_fakes.user_id, | ||||
|             '', | ||||
|             identity_fakes.project_id, | ||||
|             '' | ||||
|             ),) | ||||
|         self.assertEqual(tuple(data), datalist) | ||||
|  | ||||
|     def test_role_assignment_list_group(self): | ||||
|  | ||||
|         self.role_assignments_mock.list.return_value = [ | ||||
|             fakes.FakeResource( | ||||
|                 None, | ||||
|                 copy.deepcopy( | ||||
|                     identity_fakes.ASSIGNMENT_WITH_DOMAIN_ID_AND_GROUP_ID), | ||||
|                 loaded=True, | ||||
|             ), | ||||
|             fakes.FakeResource( | ||||
|                 None, | ||||
|                 copy.deepcopy( | ||||
|                     identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_GROUP_ID), | ||||
|                 loaded=True, | ||||
|             ), | ||||
|         ] | ||||
|  | ||||
|         arglist = [ | ||||
|             '--group', identity_fakes.group_name | ||||
|         ] | ||||
|         verifylist = [ | ||||
|             ('user', None), | ||||
|             ('group', identity_fakes.group_name), | ||||
|             ('domain', None), | ||||
|             ('project', None), | ||||
|             ('role', None), | ||||
|             ('effective', False), | ||||
|         ] | ||||
|         parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||||
|  | ||||
|         # DisplayCommandBase.take_action() returns two tuples | ||||
|         columns, data = self.cmd.take_action(parsed_args) | ||||
|  | ||||
|         self.role_assignments_mock.list.assert_called_with( | ||||
|             domain=None, | ||||
|             group=self.groups_mock.get(), | ||||
|             effective=False, | ||||
|             project=None, | ||||
|             role=None, | ||||
|             user=None) | ||||
|  | ||||
|         collist = ('Role', 'User', 'Group', 'Project', 'Domain') | ||||
|         self.assertEqual(columns, collist) | ||||
|         datalist = (( | ||||
|             identity_fakes.role_id, | ||||
|             '', | ||||
|             identity_fakes.group_id, | ||||
|             '', | ||||
|             identity_fakes.domain_id | ||||
|         ), (identity_fakes.role_id, | ||||
|             '', | ||||
|             identity_fakes.group_id, | ||||
|             identity_fakes.project_id, | ||||
|             '' | ||||
|             ),) | ||||
|         self.assertEqual(tuple(data), datalist) | ||||
|  | ||||
|     def test_role_assignment_list_domain(self): | ||||
|  | ||||
|         self.role_assignments_mock.list.return_value = [ | ||||
|             fakes.FakeResource( | ||||
|                 None, | ||||
|                 copy.deepcopy( | ||||
|                     identity_fakes.ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID), | ||||
|                 loaded=True, | ||||
|             ), | ||||
|             fakes.FakeResource( | ||||
|                 None, | ||||
|                 copy.deepcopy( | ||||
|                     identity_fakes.ASSIGNMENT_WITH_DOMAIN_ID_AND_GROUP_ID), | ||||
|                 loaded=True, | ||||
|             ), | ||||
|         ] | ||||
|  | ||||
|         arglist = [ | ||||
|             '--domain', identity_fakes.domain_name | ||||
|         ] | ||||
|         verifylist = [ | ||||
|             ('user', None), | ||||
|             ('group', None), | ||||
|             ('domain', identity_fakes.domain_name), | ||||
|             ('project', None), | ||||
|             ('role', None), | ||||
|             ('effective', False), | ||||
|         ] | ||||
|         parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||||
|  | ||||
|         # DisplayCommandBase.take_action() returns two tuples | ||||
|         columns, data = self.cmd.take_action(parsed_args) | ||||
|  | ||||
|         self.role_assignments_mock.list.assert_called_with( | ||||
|             domain=self.domains_mock.get(), | ||||
|             group=None, | ||||
|             effective=False, | ||||
|             project=None, | ||||
|             role=None, | ||||
|             user=None) | ||||
|  | ||||
|         collist = ('Role', 'User', 'Group', 'Project', 'Domain') | ||||
|         self.assertEqual(columns, collist) | ||||
|         datalist = (( | ||||
|             identity_fakes.role_id, | ||||
|             identity_fakes.user_id, | ||||
|             '', | ||||
|             '', | ||||
|             identity_fakes.domain_id | ||||
|         ), (identity_fakes.role_id, | ||||
|             '', | ||||
|             identity_fakes.group_id, | ||||
|             '', | ||||
|             identity_fakes.domain_id | ||||
|             ),) | ||||
|         self.assertEqual(tuple(data), datalist) | ||||
|  | ||||
|     def test_role_assignment_list_project(self): | ||||
|  | ||||
|         self.role_assignments_mock.list.return_value = [ | ||||
|             fakes.FakeResource( | ||||
|                 None, | ||||
|                 copy.deepcopy( | ||||
|                     identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID), | ||||
|                 loaded=True, | ||||
|             ), | ||||
|             fakes.FakeResource( | ||||
|                 None, | ||||
|                 copy.deepcopy( | ||||
|                     identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_GROUP_ID), | ||||
|                 loaded=True, | ||||
|             ), | ||||
|         ] | ||||
|  | ||||
|         arglist = [ | ||||
|             '--project', identity_fakes.project_name | ||||
|         ] | ||||
|         verifylist = [ | ||||
|             ('user', None), | ||||
|             ('group', None), | ||||
|             ('domain', None), | ||||
|             ('project', identity_fakes.project_name), | ||||
|             ('role', None), | ||||
|             ('effective', False), | ||||
|         ] | ||||
|         parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||||
|  | ||||
|         # DisplayCommandBase.take_action() returns two tuples | ||||
|         columns, data = self.cmd.take_action(parsed_args) | ||||
|  | ||||
|         self.role_assignments_mock.list.assert_called_with( | ||||
|             domain=None, | ||||
|             group=None, | ||||
|             effective=False, | ||||
|             project=self.projects_mock.get(), | ||||
|             role=None, | ||||
|             user=None) | ||||
|  | ||||
|         collist = ('Role', 'User', 'Group', 'Project', 'Domain') | ||||
|         self.assertEqual(columns, collist) | ||||
|         datalist = (( | ||||
|             identity_fakes.role_id, | ||||
|             identity_fakes.user_id, | ||||
|             '', | ||||
|             identity_fakes.project_id, | ||||
|             '' | ||||
|         ), (identity_fakes.role_id, | ||||
|             '', | ||||
|             identity_fakes.group_id, | ||||
|             identity_fakes.project_id, | ||||
|             '' | ||||
|             ),) | ||||
|         self.assertEqual(tuple(data), datalist) | ||||
|  | ||||
|     def test_role_assignment_list_effective(self): | ||||
|  | ||||
|         self.role_assignments_mock.list.return_value = [ | ||||
|             fakes.FakeResource( | ||||
|                 None, | ||||
|                 copy.deepcopy( | ||||
|                     identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID), | ||||
|                 loaded=True, | ||||
|             ), | ||||
|             fakes.FakeResource( | ||||
|                 None, | ||||
|                 copy.deepcopy( | ||||
|                     identity_fakes.ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID), | ||||
|                 loaded=True, | ||||
|             ), | ||||
|         ] | ||||
|  | ||||
|         arglist = ['--effective'] | ||||
|         verifylist = [ | ||||
|             ('user', None), | ||||
|             ('group', None), | ||||
|             ('domain', None), | ||||
|             ('project', None), | ||||
|             ('role', None), | ||||
|             ('effective', True), | ||||
|         ] | ||||
|         parsed_args = self.check_parser(self.cmd, arglist, verifylist) | ||||
|  | ||||
|         # DisplayCommandBase.take_action() returns two tuples | ||||
|         columns, data = self.cmd.take_action(parsed_args) | ||||
|  | ||||
|         self.role_assignments_mock.list.assert_called_with( | ||||
|             domain=None, | ||||
|             group=None, | ||||
|             effective=True, | ||||
|             project=None, | ||||
|             role=None, | ||||
|             user=None) | ||||
|  | ||||
|         collist = ('Role', 'User', 'Group', 'Project', 'Domain') | ||||
|         self.assertEqual(columns, collist) | ||||
|         datalist = (( | ||||
|             identity_fakes.role_id, | ||||
|             identity_fakes.user_id, | ||||
|             '', | ||||
|             identity_fakes.project_id, | ||||
|             '' | ||||
|         ), (identity_fakes.role_id, | ||||
|             identity_fakes.user_id, | ||||
|             '', | ||||
|             '', | ||||
|             identity_fakes.domain_id, | ||||
|             ),) | ||||
|         self.assertEqual(tuple(data), datalist) | ||||
| @@ -228,6 +228,7 @@ openstack.identity.v3 = | ||||
|     role_remove = openstackclient.identity.v3.role:RemoveRole | ||||
|     role_show = openstackclient.identity.v3.role:ShowRole | ||||
|     role_set = openstackclient.identity.v3.role:SetRole | ||||
|     role_assignment_list = openstackclient.identity.v3.role_assignment:ListRoleAssignment | ||||
|  | ||||
|     service_create = openstackclient.identity.v3.service:CreateService | ||||
|     service_delete = openstackclient.identity.v3.service:DeleteService | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Jenkins
					Jenkins