Merge "Add --name-lookup-one-by-one option to server list"

This commit is contained in:
Zuul 2018-11-08 19:07:02 +00:00 committed by Gerrit Code Review
commit c3a60e1173
3 changed files with 91 additions and 23 deletions

View File

@ -1087,11 +1087,22 @@ class ListServer(command.Lister):
default=False,
help=_('List additional fields in output'),
)
parser.add_argument(
name_lookup_group = parser.add_mutually_exclusive_group()
name_lookup_group.add_argument(
'-n', '--no-name-lookup',
action='store_true',
default=False,
help=_('Skip flavor and image name lookup.'),
help=_('Skip flavor and image name lookup.'
'Mutually exclusive with "--name-lookup-one-by-one"'
' option.'),
)
name_lookup_group.add_argument(
'--name-lookup-one-by-one',
action='store_true',
default=False,
help=_('When looking up flavor and image names, look them up'
'one by one as needed instead of all together (default). '
'Mutually exclusive with "--no-name-lookup|-n" option.'),
)
parser.add_argument(
'--marker',
@ -1266,28 +1277,43 @@ class ListServer(command.Lister):
limit=parsed_args.limit)
images = {}
# Create a dict that maps image_id to image object.
# Needed so that we can display the "Image Name" column.
# "Image Name" is not crucial, so we swallow any exceptions.
if data and not parsed_args.no_name_lookup:
try:
images_list = self.app.client_manager.image.images.list()
for i in images_list:
images[i.id] = i
except Exception:
pass
flavors = {}
# Create a dict that maps flavor_id to flavor object.
# Needed so that we can display the "Flavor Name" column.
# "Flavor Name" is not crucial, so we swallow any exceptions.
if data and not parsed_args.no_name_lookup:
try:
flavors_list = compute_client.flavors.list(is_public=None)
for i in flavors_list:
flavors[i.id] = i
except Exception:
pass
# Create a dict that maps image_id to image object.
# Needed so that we can display the "Image Name" column.
# "Image Name" is not crucial, so we swallow any exceptions.
if parsed_args.name_lookup_one_by_one or image_id:
for i_id in set(filter(lambda x: x is not None,
(s.image.get('id') for s in data))):
try:
images[i_id] = image_client.images.get(i_id)
except Exception:
pass
else:
try:
images_list = image_client.images.list()
for i in images_list:
images[i.id] = i
except Exception:
pass
# Create a dict that maps flavor_id to flavor object.
# Needed so that we can display the "Flavor Name" column.
# "Flavor Name" is not crucial, so we swallow any exceptions.
if parsed_args.name_lookup_one_by_one or flavor_id:
for f_id in set(filter(lambda x: x is not None,
(s.flavor.get('id') for s in data))):
try:
flavors[f_id] = compute_client.flavors.get(f_id)
except Exception:
pass
else:
try:
flavors_list = compute_client.flavors.list(is_public=None)
for i in flavors_list:
flavors[i.id] = i
except Exception:
pass
# Populate image_name, image_id, flavor_name and flavor_id attributes
# of server objects so that we can display those columns.

View File

@ -2086,12 +2086,18 @@ class TestServerList(TestServer):
('all_projects', False),
('long', False),
('deleted', False),
('name_lookup_one_by_one', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.servers_mock.list.assert_called_with(**self.kwargs)
self.images_mock.list.assert_called()
self.flavors_mock.list.assert_called()
# we did not pass image or flavor, so gets on those must be absent
self.assertFalse(self.flavors_mock.get.call_count)
self.assertFalse(self.images_mock.get.call_count)
self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data), tuple(data))
@ -2162,6 +2168,28 @@ class TestServerList(TestServer):
self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data_no_name_lookup), tuple(data))
def test_server_list_name_lookup_one_by_one(self):
arglist = [
'--name-lookup-one-by-one'
]
verifylist = [
('all_projects', False),
('no_name_lookup', False),
('name_lookup_one_by_one', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.servers_mock.list.assert_called_with(**self.kwargs)
self.assertFalse(self.images_mock.list.call_count)
self.assertFalse(self.flavors_mock.list.call_count)
self.images_mock.get.assert_called()
self.flavors_mock.get.assert_called()
self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data), tuple(data))
def test_server_list_with_image(self):
arglist = [
@ -2194,7 +2222,7 @@ class TestServerList(TestServer):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.flavors_mock.get.assert_called_with(self.flavor.id)
self.flavors_mock.get.has_calls(self.flavor.id)
self.search_opts['flavor'] = self.flavor.id
self.servers_mock.list.assert_called_with(**self.kwargs)

View File

@ -0,0 +1,14 @@
---
features:
- |
Add ``--name-lookup-one-by-one`` option to the ``server list`` command
that is (mutually exclusive with ``-n | --no-name-lookup``).
When provided, the names of images and flavors will be resolved one by one
only for those images and flavors that are needed to display the obtained
list of servers instead of fetching all the images and flavors.
Depending on amount of images in your deployment this can speed up the
execution of this command.
- |
When given ``--image`` or ``--flavor`` argument, the ``server list``
command now resolves only single image or flavor instead of fetching
all the images or flavors for name lookup purposes.