Fix BFV server list handling with --name-lookup-one-by-one

When the --name-lookup-one-by-one option passed to the 'server list'
command, the image and flavor names will be looked up for each
server being listed instead of fetching all image/flavor names.

The current code assumes all servers have an image attribute, but
servers booted from volumes have no image, so the following error is
raised when listing BFV servers with --name-lookup-one-by-one:

  AttributeError: ('unicode'|'str') object has no attribute 'get'

The error occurs when the code attempts server.image.get('id').

This fixes the --name-lookup-one-by-one code not to assume an image
for a server. The unit tests for 'server list' have also been
robustified to feature one BFV server to enhance our test coverage.

Story: #2006063
Task: #34777

Change-Id: I312c971346c7ded93f6fcaa515098554b8580295
This commit is contained in:
melanie witt 2019-06-28 18:17:10 +00:00
parent eada2db332
commit bfc34e11b3
2 changed files with 30 additions and 9 deletions

View File

@ -1350,9 +1350,12 @@ class ListServer(command.Lister):
# 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.
# The 'image' attribute can be an empty string if the server was
# booted from a volume.
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))):
(s.image.get('id') for s in data
if s.image))):
try:
images[i_id] = image_client.images.get(i_id)
except Exception:

View File

@ -14,6 +14,7 @@
#
import argparse
import collections
import copy
import getpass
import mock
@ -65,9 +66,22 @@ class TestServer(compute_fakes.TestComputev2):
self.methods = {}
def setup_servers_mock(self, count):
servers = compute_fakes.FakeServer.create_servers(attrs=self.attrs,
methods=self.methods,
count=count)
# If we are creating more than one server, make one of them
# boot-from-volume
include_bfv = count > 1
servers = compute_fakes.FakeServer.create_servers(
attrs=self.attrs,
methods=self.methods,
count=count - 1 if include_bfv else count
)
if include_bfv:
attrs = copy.deepcopy(self.attrs)
attrs['image'] = ''
bfv_server = compute_fakes.FakeServer.create_one_server(
attrs=attrs,
methods=self.methods
)
servers.append(bfv_server)
# This is the return value for utils.find_resource()
self.servers_mock.get = compute_fakes.FakeServer.get_servers(servers,
@ -2129,7 +2143,8 @@ class TestServerList(TestServer):
Image = collections.namedtuple('Image', 'id name')
self.images_mock.list.return_value = [
Image(id=s.image['id'], name=self.image.name)
for s in self.servers
# Image will be an empty string if boot-from-volume
for s in self.servers if s.image
]
Flavor = collections.namedtuple('Flavor', 'id name')
@ -2144,7 +2159,8 @@ class TestServerList(TestServer):
s.name,
s.status,
server._format_servers_list_networks(s.networks),
self.image.name,
# Image will be an empty string if boot-from-volume
self.image.name if s.image else s.image,
self.flavor.name,
))
self.data_long.append((
@ -2156,8 +2172,9 @@ class TestServerList(TestServer):
getattr(s, 'OS-EXT-STS:power_state')
),
server._format_servers_list_networks(s.networks),
self.image.name,
s.image['id'],
# Image will be an empty string if boot-from-volume
self.image.name if s.image else s.image,
s.image['id'] if s.image else s.image,
self.flavor.name,
s.flavor['id'],
getattr(s, 'OS-EXT-AZ:availability_zone'),
@ -2169,7 +2186,8 @@ class TestServerList(TestServer):
s.name,
s.status,
server._format_servers_list_networks(s.networks),
s.image['id'],
# Image will be an empty string if boot-from-volume
s.image['id'] if s.image else s.image,
s.flavor['id']
))