Skip to content

Commit e782f49

Browse files
pshcheloDean Troyer
authored andcommitted
Add --name-lookup-one-by-one option to server list
usually in a big cloud there are many images and flavors, while each given project might use only some of those. This patch introduces '--name-lookup-one-by-one' argument to server list command (mutually exclusive with '--no-name-lookup') When provided (or either '--image' or '--flavor' is specified) to the `server list` command, name resolving for corresponding entity is now using targeted GET commands instead of full entities list. In some situations this can significantly speedup the execution of the `server list` command by reducing the number of API requests performed. Change-Id: I59cbf3f75c55e5d3747654edcc9be86ad954cf40 Story: #2002039 Task: #19682
1 parent b9fab84 commit e782f49

3 files changed

Lines changed: 91 additions & 23 deletions

File tree

openstackclient/compute/v2/server.py

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,11 +1044,22 @@ def get_parser(self, prog_name):
10441044
default=False,
10451045
help=_('List additional fields in output'),
10461046
)
1047-
parser.add_argument(
1047+
name_lookup_group = parser.add_mutually_exclusive_group()
1048+
name_lookup_group.add_argument(
10481049
'-n', '--no-name-lookup',
10491050
action='store_true',
10501051
default=False,
1051-
help=_('Skip flavor and image name lookup.'),
1052+
help=_('Skip flavor and image name lookup.'
1053+
'Mutually exclusive with "--name-lookup-one-by-one"'
1054+
' option.'),
1055+
)
1056+
name_lookup_group.add_argument(
1057+
'--name-lookup-one-by-one',
1058+
action='store_true',
1059+
default=False,
1060+
help=_('When looking up flavor and image names, look them up'
1061+
'one by one as needed instead of all together (default). '
1062+
'Mutually exclusive with "--no-name-lookup|-n" option.'),
10521063
)
10531064
parser.add_argument(
10541065
'--marker',
@@ -1223,28 +1234,43 @@ def take_action(self, parsed_args):
12231234
limit=parsed_args.limit)
12241235

12251236
images = {}
1226-
# Create a dict that maps image_id to image object.
1227-
# Needed so that we can display the "Image Name" column.
1228-
# "Image Name" is not crucial, so we swallow any exceptions.
1229-
if data and not parsed_args.no_name_lookup:
1230-
try:
1231-
images_list = self.app.client_manager.image.images.list()
1232-
for i in images_list:
1233-
images[i.id] = i
1234-
except Exception:
1235-
pass
1236-
12371237
flavors = {}
1238-
# Create a dict that maps flavor_id to flavor object.
1239-
# Needed so that we can display the "Flavor Name" column.
1240-
# "Flavor Name" is not crucial, so we swallow any exceptions.
12411238
if data and not parsed_args.no_name_lookup:
1242-
try:
1243-
flavors_list = compute_client.flavors.list(is_public=None)
1244-
for i in flavors_list:
1245-
flavors[i.id] = i
1246-
except Exception:
1247-
pass
1239+
# Create a dict that maps image_id to image object.
1240+
# Needed so that we can display the "Image Name" column.
1241+
# "Image Name" is not crucial, so we swallow any exceptions.
1242+
if parsed_args.name_lookup_one_by_one or image_id:
1243+
for i_id in set(filter(lambda x: x is not None,
1244+
(s.image.get('id') for s in data))):
1245+
try:
1246+
images[i_id] = image_client.images.get(i_id)
1247+
except Exception:
1248+
pass
1249+
else:
1250+
try:
1251+
images_list = image_client.images.list()
1252+
for i in images_list:
1253+
images[i.id] = i
1254+
except Exception:
1255+
pass
1256+
1257+
# Create a dict that maps flavor_id to flavor object.
1258+
# Needed so that we can display the "Flavor Name" column.
1259+
# "Flavor Name" is not crucial, so we swallow any exceptions.
1260+
if parsed_args.name_lookup_one_by_one or flavor_id:
1261+
for f_id in set(filter(lambda x: x is not None,
1262+
(s.flavor.get('id') for s in data))):
1263+
try:
1264+
flavors[f_id] = compute_client.flavors.get(f_id)
1265+
except Exception:
1266+
pass
1267+
else:
1268+
try:
1269+
flavors_list = compute_client.flavors.list(is_public=None)
1270+
for i in flavors_list:
1271+
flavors[i.id] = i
1272+
except Exception:
1273+
pass
12481274

12491275
# Populate image_name, image_id, flavor_name and flavor_id attributes
12501276
# of server objects so that we can display those columns.

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

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1938,12 +1938,18 @@ def test_server_list_no_option(self):
19381938
('all_projects', False),
19391939
('long', False),
19401940
('deleted', False),
1941+
('name_lookup_one_by_one', False),
19411942
]
19421943
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
19431944

19441945
columns, data = self.cmd.take_action(parsed_args)
19451946

19461947
self.servers_mock.list.assert_called_with(**self.kwargs)
1948+
self.images_mock.list.assert_called()
1949+
self.flavors_mock.list.assert_called()
1950+
# we did not pass image or flavor, so gets on those must be absent
1951+
self.assertFalse(self.flavors_mock.get.call_count)
1952+
self.assertFalse(self.images_mock.get.call_count)
19471953
self.assertEqual(self.columns, columns)
19481954
self.assertEqual(tuple(self.data), tuple(data))
19491955

@@ -2014,6 +2020,28 @@ def test_server_list_n_option(self):
20142020
self.assertEqual(self.columns, columns)
20152021
self.assertEqual(tuple(self.data_no_name_lookup), tuple(data))
20162022

2023+
def test_server_list_name_lookup_one_by_one(self):
2024+
arglist = [
2025+
'--name-lookup-one-by-one'
2026+
]
2027+
verifylist = [
2028+
('all_projects', False),
2029+
('no_name_lookup', False),
2030+
('name_lookup_one_by_one', True),
2031+
]
2032+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
2033+
2034+
columns, data = self.cmd.take_action(parsed_args)
2035+
2036+
self.servers_mock.list.assert_called_with(**self.kwargs)
2037+
self.assertFalse(self.images_mock.list.call_count)
2038+
self.assertFalse(self.flavors_mock.list.call_count)
2039+
self.images_mock.get.assert_called()
2040+
self.flavors_mock.get.assert_called()
2041+
2042+
self.assertEqual(self.columns, columns)
2043+
self.assertEqual(tuple(self.data), tuple(data))
2044+
20172045
def test_server_list_with_image(self):
20182046

20192047
arglist = [
@@ -2046,7 +2074,7 @@ def test_server_list_with_flavor(self):
20462074
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
20472075
columns, data = self.cmd.take_action(parsed_args)
20482076

2049-
self.flavors_mock.get.assert_called_with(self.flavor.id)
2077+
self.flavors_mock.get.has_calls(self.flavor.id)
20502078

20512079
self.search_opts['flavor'] = self.flavor.id
20522080
self.servers_mock.list.assert_called_with(**self.kwargs)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
features:
3+
- |
4+
Add ``--name-lookup-one-by-one`` option to the ``server list`` command
5+
that is (mutually exclusive with ``-n | --no-name-lookup``).
6+
When provided, the names of images and flavors will be resolved one by one
7+
only for those images and flavors that are needed to display the obtained
8+
list of servers instead of fetching all the images and flavors.
9+
Depending on amount of images in your deployment this can speed up the
10+
execution of this command.
11+
- |
12+
When given ``--image`` or ``--flavor`` argument, the ``server list``
13+
command now resolves only single image or flavor instead of fetching
14+
all the images or flavors for name lookup purposes.

0 commit comments

Comments
 (0)