Fix 'openstack keypair list --project <project>'
The --project option of 'openstack keypair list' is supposed to filter keypairs by a project but has not been working and instead returns keypairs from all projects. The reason appears to be because it uses a request for a user list filtered by project but tenant_id/project_id is not a valid filter for GET /users. This fixes the issue by requesting role assignments for the specified project and then requesting keypairs for users with a role in the project. This change depends on a recent openstacksdk bug fix change Ic552dee83d56278d2b866de0cb365a0c394fe26a which fixed the user_id query parameter for the compute /os-keypairs APIs. The bug fix was released in openstacksdk 4.4.0. Closes-Bug: #2096947 Change-Id: Ibb5757766e3040e58d64388b95678fab9b2b6f23
This commit is contained in:
parent
616d6f3a29
commit
d123be0819
@ -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'):
|
||||
|
@ -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'])
|
||||
|
@ -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,
|
||||
): ...
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user