diff --git a/openstackclient/compute/v2/keypair.py b/openstackclient/compute/v2/keypair.py index d10939b4fc..97dd04f4e7 100644 --- a/openstackclient/compute/v2/keypair.py +++ b/openstackclient/compute/v2/keypair.py @@ -300,6 +300,7 @@ class ListKeypair(command.Lister): def take_action(self, parsed_args): compute_client = self.app.client_manager.compute identity_client = self.app.client_manager.identity + identity_sdk_client = self.app.client_manager.sdk_connection.identity kwargs = {} @@ -345,11 +346,17 @@ class ListKeypair(command.Lister): parsed_args.project, parsed_args.project_domain, ).id - users = identity_client.users.list(tenant_id=project) + assignments = identity_sdk_client.role_assignments( + scope_project_id=project + ) + user_ids = set() + for assignment in assignments: + if assignment.user: + user_ids.add(assignment.user['id']) data = [] - for user in users: - kwargs['user_id'] = user.id + for user_id in user_ids: + kwargs['user_id'] = user_id data.extend(compute_client.keypairs(**kwargs)) elif parsed_args.user: if not sdk_utils.supports_microversion(compute_client, '2.10'): diff --git a/openstackclient/tests/functional/compute/v2/test_keypair.py b/openstackclient/tests/functional/compute/v2/test_keypair.py index 4b178cb723..2e01b1bed2 100644 --- a/openstackclient/tests/functional/compute/v2/test_keypair.py +++ b/openstackclient/tests/functional/compute/v2/test_keypair.py @@ -21,12 +21,18 @@ from openstackclient.tests.functional import base class KeypairBase(base.TestCase): """Methods for functional tests.""" - def keypair_create(self, name=data_utils.rand_uuid()): + def keypair_create(self, name=data_utils.rand_uuid(), user=None): """Create keypair and add cleanup.""" - raw_output = self.openstack('keypair create ' + name) - self.addCleanup(self.keypair_delete, name, True) + cmd = 'keypair create ' + name + if user is not None: + cmd += ' --user ' + user + raw_output = self.openstack(cmd) + self.addCleanup( + self.keypair_delete, name, ignore_exceptions=True, user=user + ) if not raw_output: self.fail('Keypair has not been created!') + return name def keypair_list(self, params=''): """Return dictionary with list of keypairs.""" @@ -34,10 +40,13 @@ class KeypairBase(base.TestCase): keypairs = self.parse_show_as_object(raw_output) return keypairs - def keypair_delete(self, name, ignore_exceptions=False): + def keypair_delete(self, name, ignore_exceptions=False, user=None): """Try to delete keypair by name.""" try: - self.openstack('keypair delete ' + name) + cmd = 'keypair delete ' + name + if user is not None: + cmd += ' --user ' + user + self.openstack(cmd) except exceptions.CommandFailed: if not ignore_exceptions: raise @@ -200,3 +209,30 @@ class KeypairTests(KeypairBase): items = self.parse_listing(raw_output) self.assert_table_structure(items, HEADERS) self.assertInOutput(self.KPName, raw_output) + + def test_keypair_list_by_project(self): + """Test keypair list by project. + + Test steps: + 1) Create keypair for admin project in setUp + 2) Create a new project + 3) Create a new user + 4) Associate the new user with the new project + 5) Create keypair for the new user + 6) List keypairs by the new project + 7) Check that only the keypair from step 5 is returned + """ + project_name = data_utils.rand_name('TestProject') + self.openstack(f'project create {project_name}') + self.addCleanup(self.openstack, f'project delete {project_name}') + user_name = data_utils.rand_name('TestUser') + self.openstack(f'user create {user_name}') + self.addCleanup(self.openstack, f'user delete {user_name}') + self.openstack( + f'role add --user {user_name} --project {project_name} member' + ) + keypair_name = self.keypair_create(user=user_name) + raw_output = self.openstack(f'keypair list --project {project_name}') + items = self.parse_listing(raw_output) + self.assertEqual(1, len(items)) + self.assertEqual(keypair_name, items[0]['Name']) diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index 1eff82d114..ef868a8e30 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -33,7 +33,7 @@ from openstack.compute.v2 import server_migration as _server_migration from openstack.compute.v2 import volume_attachment as _volume_attachment from openstackclient.tests.unit import fakes -from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes from openstackclient.tests.unit.image.v2 import fakes as image_fakes from openstackclient.tests.unit.network.v2 import fakes as network_fakes from openstackclient.tests.unit import utils @@ -121,10 +121,10 @@ class FakeClientMixin: class TestComputev2( + identity_fakes.FakeClientMixin, network_fakes.FakeClientMixin, image_fakes.FakeClientMixin, volume_fakes.FakeClientMixin, - identity_fakes.FakeClientMixin, FakeClientMixin, utils.TestCommand, ): ... diff --git a/openstackclient/tests/unit/compute/v2/test_keypair.py b/openstackclient/tests/unit/compute/v2/test_keypair.py index 85a4547a63..4eaaf4c9b0 100644 --- a/openstackclient/tests/unit/compute/v2/test_keypair.py +++ b/openstackclient/tests/unit/compute/v2/test_keypair.py @@ -18,6 +18,7 @@ import uuid from openstack.compute.v2 import keypair as _keypair from openstack.identity.v3 import project as _project +from openstack.identity.v3 import role_assignment as _role_assignment from openstack.identity.v3 import user as _user from openstack.test import fakes as sdk_fakes from osc_lib import exceptions @@ -529,13 +530,17 @@ class TestKeypairList(TestKeypair): def test_keypair_list_with_project(self): self.set_compute_api_version('2.35') - projects_mock = self.identity_client.tenants + projects_mock = self.identity_client.projects projects_mock.reset_mock() projects_mock.get.return_value = self._project - users_mock = self.identity_client.users - users_mock.reset_mock() - users_mock.list.return_value = [self._user] + role_assignments_mock = self.identity_sdk_client.role_assignments + role_assignments_mock.reset_mock() + assignment = sdk_fakes.generate_fake_resource( + _role_assignment.RoleAssignment + ) + assignment.user = self._user + role_assignments_mock.return_value = [assignment] arglist = ['--project', self._project.name] verifylist = [('project', self._project.name)] @@ -544,7 +549,9 @@ class TestKeypairList(TestKeypair): columns, data = self.cmd.take_action(parsed_args) projects_mock.get.assert_called_with(self._project.name) - users_mock.list.assert_called_with(tenant_id=self._project.id) + role_assignments_mock.assert_called_with( + scope_project_id=self._project.id + ) self.compute_client.keypairs.assert_called_with( user_id=self._user.id, ) diff --git a/requirements.txt b/requirements.txt index c31de42a70..bf22833287 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0 cryptography>=2.7 # BSD/Apache-2.0 cliff>=3.5.0 # Apache-2.0 iso8601>=0.1.11 # MIT -openstacksdk>=3.3.0 # Apache-2.0 +openstacksdk>=4.4.0 # Apache-2.0 osc-lib>=2.3.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 python-keystoneclient>=3.22.0 # Apache-2.0