Skip to content

Commit 5645fad

Browse files
committed
Add support for 'keypairs list --project' parameter
It would be lovely to do this server side but doing so requires a new microversion, a blueprint and a spec. This is less performant but should do the trick for the odd time users want to do this. Change-Id: I26e7d38966304dd67be5da8ed0bb24f87191b82f Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
1 parent 98a0016 commit 5645fad

3 files changed

Lines changed: 130 additions & 15 deletions

File tree

openstackclient/compute/v2/keypair.py

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,8 @@ class ListKeypair(command.Lister):
166166

167167
def get_parser(self, prog_name):
168168
parser = super().get_parser(prog_name)
169-
parser.add_argument(
169+
user_group = parser.add_mutually_exclusive_group()
170+
user_group.add_argument(
170171
'--user',
171172
metavar='<user>',
172173
help=_(
@@ -175,29 +176,60 @@ def get_parser(self, prog_name):
175176
),
176177
)
177178
identity_common.add_user_domain_option_to_parser(parser)
179+
user_group.add_argument(
180+
'--project',
181+
metavar='<project>',
182+
help=_(
183+
'Show keypairs for all users associated with project '
184+
'(admin only) (name or ID). '
185+
'Requires ``--os-compute-api-version`` 2.10 or greater.'
186+
),
187+
)
188+
identity_common.add_project_domain_option_to_parser(parser)
178189
return parser
179190

180191
def take_action(self, parsed_args):
181192
compute_client = self.app.client_manager.compute
182193
identity_client = self.app.client_manager.identity
183194

184-
kwargs = {}
195+
if parsed_args.project:
196+
if compute_client.api_version < api_versions.APIVersion('2.10'):
197+
msg = _(
198+
'--os-compute-api-version 2.10 or greater is required to '
199+
'support the --project option'
200+
)
201+
raise exceptions.CommandError(msg)
185202

186-
if parsed_args.user:
203+
# NOTE(stephenfin): This is done client side because nova doesn't
204+
# currently support doing so server-side. If this is slow, we can
205+
# think about spinning up a threadpool or similar.
206+
project = identity_common.find_project(
207+
identity_client,
208+
parsed_args.project,
209+
parsed_args.project_domain,
210+
).id
211+
users = identity_client.users.list(tenant_id=project)
212+
213+
data = []
214+
for user in users:
215+
data.extend(compute_client.keypairs.list(user_id=user.id))
216+
elif parsed_args.user:
187217
if compute_client.api_version < api_versions.APIVersion('2.10'):
188218
msg = _(
189219
'--os-compute-api-version 2.10 or greater is required to '
190220
'support the --user option'
191221
)
192222
raise exceptions.CommandError(msg)
193223

194-
kwargs['user_id'] = identity_common.find_user(
224+
user = identity_common.find_user(
195225
identity_client,
196226
parsed_args.user,
197227
parsed_args.user_domain,
198-
).id
228+
)
199229

200-
data = compute_client.keypairs.list(**kwargs)
230+
data = compute_client.keypairs.list(user_id=user.id)
231+
else:
232+
data = compute_client.keypairs.list()
201233

202234
columns = (
203235
"Name",

openstackclient/tests/unit/compute/v2/test_keypair.py

Lines changed: 86 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -310,14 +310,6 @@ class TestKeypairList(TestKeypair):
310310
def setUp(self):
311311
super(TestKeypairList, self).setUp()
312312

313-
self.users_mock = self.app.client_manager.identity.users
314-
self.users_mock.reset_mock()
315-
self.users_mock.get.return_value = fakes.FakeResource(
316-
None,
317-
copy.deepcopy(identity_fakes.USER),
318-
loaded=True,
319-
)
320-
321313
self.keypairs_mock.list.return_value = self.keypairs
322314

323315
# Get the command object to test
@@ -378,6 +370,14 @@ def test_keypair_list_with_user(self):
378370
self.app.client_manager.compute.api_version = \
379371
api_versions.APIVersion('2.10')
380372

373+
users_mock = self.app.client_manager.identity.users
374+
users_mock.reset_mock()
375+
users_mock.get.return_value = fakes.FakeResource(
376+
None,
377+
copy.deepcopy(identity_fakes.USER),
378+
loaded=True,
379+
)
380+
381381
arglist = [
382382
'--user', identity_fakes.user_name,
383383
]
@@ -388,7 +388,7 @@ def test_keypair_list_with_user(self):
388388

389389
columns, data = self.cmd.take_action(parsed_args)
390390

391-
self.users_mock.get.assert_called_with(identity_fakes.user_name)
391+
users_mock.get.assert_called_with(identity_fakes.user_name)
392392
self.keypairs_mock.list.assert_called_with(
393393
user_id=identity_fakes.user_id,
394394
)
@@ -423,6 +423,83 @@ def test_keypair_list_with_user_pre_v210(self):
423423
self.assertIn(
424424
'--os-compute-api-version 2.10 or greater is required', str(ex))
425425

426+
def test_keypair_list_with_project(self):
427+
428+
# Filtering by user is support for nova api 2.10 or above
429+
self.app.client_manager.compute.api_version = \
430+
api_versions.APIVersion('2.10')
431+
432+
projects_mock = self.app.client_manager.identity.tenants
433+
projects_mock.reset_mock()
434+
projects_mock.get.return_value = fakes.FakeResource(
435+
None,
436+
copy.deepcopy(identity_fakes.PROJECT),
437+
loaded=True,
438+
)
439+
440+
users_mock = self.app.client_manager.identity.users
441+
users_mock.reset_mock()
442+
users_mock.list.return_value = [
443+
fakes.FakeResource(
444+
None,
445+
copy.deepcopy(identity_fakes.USER),
446+
loaded=True,
447+
),
448+
]
449+
450+
arglist = ['--project', identity_fakes.project_name]
451+
verifylist = [('project', identity_fakes.project_name)]
452+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
453+
454+
columns, data = self.cmd.take_action(parsed_args)
455+
456+
projects_mock.get.assert_called_with(identity_fakes.project_name)
457+
users_mock.list.assert_called_with(tenant_id=identity_fakes.project_id)
458+
self.keypairs_mock.list.assert_called_with(
459+
user_id=identity_fakes.user_id,
460+
)
461+
462+
self.assertEqual(('Name', 'Fingerprint', 'Type'), columns)
463+
self.assertEqual(
464+
((
465+
self.keypairs[0].name,
466+
self.keypairs[0].fingerprint,
467+
self.keypairs[0].type,
468+
), ),
469+
tuple(data)
470+
)
471+
472+
def test_keypair_list_with_project_pre_v210(self):
473+
474+
self.app.client_manager.compute.api_version = \
475+
api_versions.APIVersion('2.9')
476+
477+
arglist = ['--project', identity_fakes.project_name]
478+
verifylist = [('project', identity_fakes.project_name)]
479+
480+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
481+
ex = self.assertRaises(
482+
exceptions.CommandError,
483+
self.cmd.take_action,
484+
parsed_args)
485+
self.assertIn(
486+
'--os-compute-api-version 2.10 or greater is required', str(ex))
487+
488+
def test_keypair_list_conflicting_user_options(self):
489+
490+
# Filtering by user is support for nova api 2.10 or above
491+
self.app.client_manager.compute.api_version = \
492+
api_versions.APIVersion('2.10')
493+
494+
arglist = [
495+
'--user', identity_fakes.user_name,
496+
'--project', identity_fakes.project_name,
497+
]
498+
499+
self.assertRaises(
500+
tests_utils.ParserException,
501+
self.check_parser, self.cmd, arglist, None)
502+
426503

427504
class TestKeypairShow(TestKeypair):
428505

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
features:
3+
- |
4+
It is now possible to list the keypairs for all users in a project using
5+
the ``--project`` parameter. This is an admin-only action by default and
6+
requires Compute API microversion 2.10 or later.

0 commit comments

Comments
 (0)