Use the compute SDK in server list

Update server list to use the compute component of the OpenStack SDK
instead of directly using the nova interface. This change depends on SDK
version 0.102.0 for automatic client-side query filters.

Change-Id: Ib9985812bfd98320b75f3a82bb594a0daa6e4d93
This commit is contained in:
Daniel Wilson 2022-10-11 00:39:39 -04:00
parent 56b0f6de0e
commit c97f73ce2a
4 changed files with 205 additions and 189 deletions

View File

@ -66,6 +66,28 @@ class PowerStateColumn(cliff_columns.FormattableColumn):
return 'N/A'
class AddressesColumn(cliff_columns.FormattableColumn):
"""Generate a formatted string of a server's addresses."""
def human_readable(self):
try:
return utils.format_dict_of_list({
k: [i['addr'] for i in v if 'addr' in i]
for k, v in self._value.items()})
except Exception:
return 'N/A'
class HostColumn(cliff_columns.FormattableColumn):
"""Generate a formatted string of a hostname."""
def human_readable(self):
if self._value is None:
return ''
return self._value
def _get_ip_address(addresses, address_type, ip_address_family):
# Old style addresses
if address_type in addresses:
@ -2287,7 +2309,7 @@ class ListServer(command.Lister):
return parser
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
compute_client = self.app.client_manager.sdk_connection.compute
identity_client = self.app.client_manager.identity
image_client = self.app.client_manager.image
@ -2312,10 +2334,11 @@ class ListServer(command.Lister):
# flavor name is given, map it to ID.
flavor_id = None
if parsed_args.flavor:
flavor_id = utils.find_resource(
compute_client.flavors,
parsed_args.flavor,
).id
flavor = compute_client.find_flavor(parsed_args.flavor)
if flavor is None:
msg = _('Unable to find flavor: %s') % parsed_args.flavor
raise exceptions.CommandError(msg)
flavor_id = flavor.id
# Nova only supports list servers searching by image ID. So if a
# image name is given, map it to ID.
@ -2331,19 +2354,21 @@ class ListServer(command.Lister):
'ip': parsed_args.ip,
'ip6': parsed_args.ip6,
'name': parsed_args.name,
'instance_name': parsed_args.instance_name,
'status': parsed_args.status,
'flavor': flavor_id,
'image': image_id,
'host': parsed_args.host,
'tenant_id': project_id,
'all_tenants': parsed_args.all_projects,
'project_id': project_id,
'all_projects': parsed_args.all_projects,
'user_id': user_id,
'deleted': parsed_args.deleted,
'changes-before': parsed_args.changes_before,
'changes-since': parsed_args.changes_since,
}
if parsed_args.instance_name is not None:
search_opts['instance_name'] = parsed_args.instance_name
if parsed_args.availability_zone:
search_opts['availability_zone'] = parsed_args.availability_zone
@ -2375,7 +2400,7 @@ class ListServer(command.Lister):
search_opts['power_state'] = power_state
if parsed_args.tags:
if compute_client.api_version < api_versions.APIVersion('2.26'):
if not sdk_utils.supports_microversion(compute_client, '2.26'):
msg = _(
'--os-compute-api-version 2.26 or greater is required to '
'support the --tag option'
@ -2385,7 +2410,7 @@ class ListServer(command.Lister):
search_opts['tags'] = ','.join(parsed_args.tags)
if parsed_args.not_tags:
if compute_client.api_version < api_versions.APIVersion('2.26'):
if not sdk_utils.supports_microversion(compute_client, '2.26'):
msg = _(
'--os-compute-api-version 2.26 or greater is required to '
'support the --not-tag option'
@ -2395,7 +2420,7 @@ class ListServer(command.Lister):
search_opts['not-tags'] = ','.join(parsed_args.not_tags)
if parsed_args.locked:
if compute_client.api_version < api_versions.APIVersion('2.73'):
if not sdk_utils.supports_microversion(compute_client, '2.73'):
msg = _(
'--os-compute-api-version 2.73 or greater is required to '
'support the --locked option'
@ -2404,7 +2429,7 @@ class ListServer(command.Lister):
search_opts['locked'] = True
elif parsed_args.unlocked:
if compute_client.api_version < api_versions.APIVersion('2.73'):
if not sdk_utils.supports_microversion(compute_client, '2.73'):
msg = _(
'--os-compute-api-version 2.73 or greater is required to '
'support the --unlocked option'
@ -2413,10 +2438,14 @@ class ListServer(command.Lister):
search_opts['locked'] = False
if parsed_args.limit is not None:
search_opts['limit'] = parsed_args.limit
search_opts['paginated'] = False
LOG.debug('search options: %s', search_opts)
if search_opts['changes-before']:
if compute_client.api_version < api_versions.APIVersion('2.66'):
if not sdk_utils.supports_microversion(compute_client, '2.66'):
msg = _('--os-compute-api-version 2.66 or later is required')
raise exceptions.CommandError(msg)
@ -2450,15 +2479,15 @@ class ListServer(command.Lister):
if parsed_args.long:
columns += (
'OS-EXT-STS:task_state',
'OS-EXT-STS:power_state',
'task_state',
'power_state',
)
column_headers += (
'Task State',
'Power State',
)
columns += ('networks',)
columns += ('addresses',)
column_headers += ('Networks',)
if parsed_args.long:
@ -2480,7 +2509,7 @@ class ListServer(command.Lister):
# microversion 2.47 puts the embedded flavor into the server response
# body but omits the id, so if not present we just expose the original
# flavor name in the output
if compute_client.api_version >= api_versions.APIVersion('2.47'):
if sdk_utils.supports_microversion(compute_client, '2.47'):
columns += ('flavor_name',)
column_headers += ('Flavor',)
else:
@ -2502,8 +2531,8 @@ class ListServer(command.Lister):
if parsed_args.long:
columns += (
'OS-EXT-AZ:availability_zone',
'OS-EXT-SRV-ATTR:host',
'availability_zone',
'hypervisor_hostname',
'metadata',
)
column_headers += (
@ -2512,40 +2541,38 @@ class ListServer(command.Lister):
'Properties',
)
marker_id = None
# support for additional columns
if parsed_args.columns:
for c in parsed_args.columns:
if c in ('Project ID', 'project_id'):
columns += ('tenant_id',)
columns += ('project_id',)
column_headers += ('Project ID',)
if c in ('User ID', 'user_id'):
columns += ('user_id',)
column_headers += ('User ID',)
if c in ('Created At', 'created_at'):
columns += ('created',)
columns += ('created_at',)
column_headers += ('Created At',)
if c in ('Security Groups', 'security_groups'):
columns += ('security_groups_name',)
column_headers += ('Security Groups',)
if c in ("Task State", "task_state"):
columns += ('OS-EXT-STS:task_state',)
columns += ('task_state',)
column_headers += ('Task State',)
if c in ("Power State", "power_state"):
columns += ('OS-EXT-STS:power_state',)
columns += ('power_state',)
column_headers += ('Power State',)
if c in ("Image ID", "image_id"):
columns += ('Image ID',)
column_headers += ('Image ID',)
if c in ("Flavor ID", "flavor_id"):
columns += ('Flavor ID',)
columns += ('flavor_id',)
column_headers += ('Flavor ID',)
if c in ('Availability Zone', "availability_zone"):
columns += ('OS-EXT-AZ:availability_zone',)
columns += ('availability_zone',)
column_headers += ('Availability Zone',)
if c in ('Host', "host"):
columns += ('OS-EXT-SRV-ATTR:host',)
columns += ('hypervisor_hostname',)
column_headers += ('Host',)
if c in ('Properties', "properties"):
columns += ('Metadata',)
@ -2555,7 +2582,7 @@ class ListServer(command.Lister):
column_headers = tuple(column_headers)
columns = tuple(columns)
if parsed_args.marker:
if parsed_args.marker is not None:
# Check if both "--marker" and "--deleted" are used.
# In that scenario a lookup is not needed as the marker
# needs to be an ID, because find_resource does not
@ -2563,16 +2590,10 @@ class ListServer(command.Lister):
if parsed_args.deleted:
marker_id = parsed_args.marker
else:
marker_id = utils.find_resource(
compute_client.servers,
parsed_args.marker,
).id
marker_id = compute_client.find_server(parsed_args.marker).id
search_opts['marker'] = marker_id
data = compute_client.servers.list(
search_opts=search_opts,
marker=marker_id,
limit=parsed_args.limit,
)
data = list(compute_client.servers(**search_opts))
images = {}
flavors = {}
@ -2627,12 +2648,12 @@ class ListServer(command.Lister):
# "Flavor Name" is not crucial, so we swallow any
# exceptions
try:
flavors[f_id] = compute_client.flavors.get(f_id)
flavors[f_id] = compute_client.find_flavor(f_id)
except Exception:
pass
else:
try:
flavors_list = compute_client.flavors.list(is_public=None)
flavors_list = compute_client.flavors(is_public=None)
for i in flavors_list:
flavors[i.id] = i
except Exception:
@ -2641,7 +2662,7 @@ class ListServer(command.Lister):
# Populate image_name, image_id, flavor_name and flavor_id attributes
# of server objects so that we can display those columns.
for s in data:
if compute_client.api_version >= api_versions.APIVersion('2.69'):
if sdk_utils.supports_microversion(compute_client, '2.69'):
# NOTE(tssurya): From 2.69, we will have the keys 'flavor'
# and 'image' missing in the server response during
# infrastructure failure situations.
@ -2650,7 +2671,7 @@ class ListServer(command.Lister):
if not hasattr(s, 'image') or not hasattr(s, 'flavor'):
continue
if 'id' in s.image:
if 'id' in s.image and s.image.id is not None:
image = images.get(s.image['id'])
if image:
s.image_name = image.name
@ -2663,7 +2684,7 @@ class ListServer(command.Lister):
s.image_name = IMAGE_STRING_FOR_BFV
s.image_id = IMAGE_STRING_FOR_BFV
if compute_client.api_version < api_versions.APIVersion('2.47'):
if not sdk_utils.supports_microversion(compute_client, '2.47'):
flavor = flavors.get(s.flavor['id'])
if flavor:
s.flavor_name = flavor.name
@ -2673,7 +2694,7 @@ class ListServer(command.Lister):
# Add a list with security group name as attribute
for s in data:
if hasattr(s, 'security_groups'):
if hasattr(s, 'security_groups') and s.security_groups is not None:
s.security_groups_name = [x["name"] for x in s.security_groups]
else:
s.security_groups_name = []
@ -2686,10 +2707,10 @@ class ListServer(command.Lister):
# it's on, providing useful information to a user in this
# situation.
if (
compute_client.api_version >= api_versions.APIVersion('2.16') and
sdk_utils.supports_microversion(compute_client, '2.16') and
parsed_args.long
):
if any([hasattr(s, 'host_status') for s in data]):
if any([s.host_status is not None for s in data]):
columns += ('Host Status',)
column_headers += ('Host Status',)
@ -2699,16 +2720,17 @@ class ListServer(command.Lister):
utils.get_item_properties(
s, columns,
mixed_case_fields=(
'OS-EXT-STS:task_state',
'OS-EXT-STS:power_state',
'OS-EXT-AZ:availability_zone',
'OS-EXT-SRV-ATTR:host',
'task_state',
'power_state',
'availability_zone',
'host',
),
formatters={
'OS-EXT-STS:power_state': PowerStateColumn,
'networks': format_columns.DictListColumn,
'power_state': PowerStateColumn,
'addresses': AddressesColumn,
'metadata': format_columns.DictColumn,
'security_groups_name': format_columns.ListColumn,
'hypervisor_hostname': HostColumn,
},
) for s in data
),

View File

@ -105,7 +105,7 @@ class ServerTests(common.ComputeTestCase):
'server list -f json --deleted --marker ' + name2
))
except exceptions.CommandFailed as e:
self.assertIn('marker [%s] not found (HTTP 400)' % (name2),
self.assertIn('marker [%s] not found' % (name2),
e.stderr.decode('utf-8'))
def test_server_list_with_changes_before(self):

View File

@ -117,6 +117,21 @@ class TestServer(compute_fakes.TestComputev2):
# Set object methods to be tested. Could be overwritten in subclass.
self.methods = {}
patcher = mock.patch.object(
sdk_utils, 'supports_microversion', return_value=True)
self.addCleanup(patcher.stop)
self.supports_microversion_mock = patcher.start()
self._set_mock_microversion(
self.app.client_manager.compute.api_version.get_string())
def _set_mock_microversion(self, mock_v):
"""Set a specific microversion for the mock supports_microversion()."""
self.supports_microversion_mock.reset_mock(return_value=True)
self.supports_microversion_mock.side_effect = (
lambda _, v:
api_versions.APIVersion(v) <= api_versions.APIVersion(mock_v))
def setup_servers_mock(self, count):
# If we are creating more than one server, make one of them
# boot-from-volume
@ -4448,32 +4463,25 @@ class _TestServerList(TestServer):
def setUp(self):
super(_TestServerList, self).setUp()
self.search_opts = {
# Default params of the core function of the command in the case of no
# commandline option specified.
self.kwargs = {
'reservation_id': None,
'ip': None,
'ip6': None,
'name': None,
'instance_name': None,
'status': None,
'flavor': None,
'image': None,
'host': None,
'tenant_id': None,
'all_tenants': False,
'project_id': None,
'all_projects': False,
'user_id': None,
'deleted': False,
'changes-since': None,
'changes-before': None,
}
# Default params of the core function of the command in the case of no
# commandline option specified.
self.kwargs = {
'search_opts': self.search_opts,
'marker': None,
'limit': None,
}
# The fake servers' attributes. Use the original attributes names in
# nova, not the ones printed by "server list" command.
self.attrs = {
@ -4488,10 +4496,6 @@ class _TestServerList(TestServer):
'Metadata': format_columns.DictColumn({}),
}
# The servers to be listed.
self.servers = self.setup_servers_mock(3)
self.servers_mock.list.return_value = self.servers
self.image = image_fakes.create_one_image()
# self.images_mock.return_value = [self.image]
@ -4499,7 +4503,12 @@ class _TestServerList(TestServer):
self.get_image_mock.return_value = self.image
self.flavor = compute_fakes.FakeFlavor.create_one_flavor()
self.flavors_mock.get.return_value = self.flavor
self.sdk_client.find_flavor.return_value = self.flavor
self.attrs['flavor'] = {'original_name': self.flavor.name}
# The servers to be listed.
self.servers = self.setup_sdk_servers_mock(3)
self.sdk_client.servers.return_value = self.servers
# Get the command object to test
self.cmd = server.ListServer(self.app, None)
@ -4518,7 +4527,7 @@ class TestServerList(_TestServerList):
]
Flavor = collections.namedtuple('Flavor', 'id name')
self.flavors_mock.list.return_value = [
self.sdk_client.flavors.return_value = [
Flavor(id=s.flavor['id'], name=self.flavor.name)
for s in self.servers
]
@ -4528,7 +4537,7 @@ class TestServerList(_TestServerList):
s.id,
s.name,
s.status,
format_columns.DictListColumn(s.networks),
server.AddressesColumn(s.addresses),
# Image will be an empty string if boot-from-volume
self.image.name if s.image else server.IMAGE_STRING_FOR_BFV,
self.flavor.name,
@ -4547,9 +4556,9 @@ class TestServerList(_TestServerList):
columns, data = self.cmd.take_action(parsed_args)
self.servers_mock.list.assert_called_with(**self.kwargs)
self.sdk_client.servers.assert_called_with(**self.kwargs)
self.images_mock.assert_called()
self.flavors_mock.list.assert_called()
self.sdk_client.flavors.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.get_image_mock.call_count)
@ -4564,14 +4573,14 @@ class TestServerList(_TestServerList):
('deleted', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.servers_mock.list.return_value = []
self.sdk_client.servers.return_value = []
self.data = ()
columns, data = self.cmd.take_action(parsed_args)
self.servers_mock.list.assert_called_with(**self.kwargs)
self.sdk_client.servers.assert_called_with(**self.kwargs)
self.images_mock.assert_not_called()
self.flavors_mock.list.assert_not_called()
self.sdk_client.flavors.assert_not_called()
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, tuple(data))
@ -4581,19 +4590,19 @@ class TestServerList(_TestServerList):
s.id,
s.name,
s.status,
getattr(s, 'OS-EXT-STS:task_state'),
getattr(s, 'task_state'),
server.PowerStateColumn(
getattr(s, 'OS-EXT-STS:power_state')
getattr(s, 'power_state')
),
format_columns.DictListColumn(s.networks),
server.AddressesColumn(s.addresses),
# Image will be an empty string if boot-from-volume
self.image.name if s.image else server.IMAGE_STRING_FOR_BFV,
s.image['id'] if s.image else server.IMAGE_STRING_FOR_BFV,
self.flavor.name,
s.flavor['id'],
getattr(s, 'OS-EXT-AZ:availability_zone'),
getattr(s, 'OS-EXT-SRV-ATTR:host'),
s.Metadata,
getattr(s, 'availability_zone'),
server.HostColumn(getattr(s, 'hypervisor_hostname')),
format_columns.DictColumn(s.metadata),
) for s in self.servers
)
arglist = [
@ -4606,12 +4615,12 @@ class TestServerList(_TestServerList):
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.sdk_client.servers.assert_called_with(**self.kwargs)
image_ids = {s.image['id'] for s in self.servers if s.image}
self.images_mock.assert_called_once_with(
id=f'in:{",".join(image_ids)}',
)
self.flavors_mock.list.assert_called_once_with(is_public=None)
self.sdk_client.flavors.assert_called_once_with(is_public=None)
self.assertEqual(self.columns_long, columns)
self.assertEqual(self.data, tuple(data))
@ -4637,7 +4646,7 @@ class TestServerList(_TestServerList):
columns, data = self.cmd.take_action(parsed_args)
self.servers_mock.list.assert_called_with(**self.kwargs)
self.sdk_client.servers.assert_called_with(**self.kwargs)
self.assertIn('Project ID', columns)
self.assertIn('User ID', columns)
self.assertIn('Created At', columns)
@ -4656,7 +4665,7 @@ class TestServerList(_TestServerList):
s.id,
s.name,
s.status,
format_columns.DictListColumn(s.networks),
server.AddressesColumn(s.addresses),
# Image will be an empty string if boot-from-volume
s.image['id'] if s.image else server.IMAGE_STRING_FOR_BFV,
s.flavor['id']
@ -4674,9 +4683,9 @@ class TestServerList(_TestServerList):
columns, data = self.cmd.take_action(parsed_args)
self.servers_mock.list.assert_called_with(**self.kwargs)
self.sdk_client.servers.assert_called_with(**self.kwargs)
self.images_mock.assert_not_called()
self.flavors_mock.list.assert_not_called()
self.sdk_client.flavors.assert_not_called()
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, tuple(data))
@ -4686,7 +4695,7 @@ class TestServerList(_TestServerList):
s.id,
s.name,
s.status,
format_columns.DictListColumn(s.networks),
server.AddressesColumn(s.addresses),
# Image will be an empty string if boot-from-volume
s.image['id'] if s.image else server.IMAGE_STRING_FOR_BFV,
s.flavor['id']
@ -4704,9 +4713,9 @@ class TestServerList(_TestServerList):
columns, data = self.cmd.take_action(parsed_args)
self.servers_mock.list.assert_called_with(**self.kwargs)
self.sdk_client.servers.assert_called_with(**self.kwargs)
self.images_mock.assert_not_called()
self.flavors_mock.list.assert_not_called()
self.sdk_client.flavors.assert_not_called()
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, tuple(data))
@ -4723,11 +4732,11 @@ class TestServerList(_TestServerList):
columns, data = self.cmd.take_action(parsed_args)
self.servers_mock.list.assert_called_with(**self.kwargs)
self.sdk_client.servers.assert_called_with(**self.kwargs)
self.images_mock.assert_not_called()
self.flavors_mock.list.assert_not_called()
self.sdk_client.flavors.assert_not_called()
self.get_image_mock.assert_called()
self.flavors_mock.get.assert_called()
self.sdk_client.find_flavor.assert_called()
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, tuple(data))
@ -4747,10 +4756,10 @@ class TestServerList(_TestServerList):
self.find_image_mock.assert_called_with(self.image.id,
ignore_missing=False)
self.search_opts['image'] = self.image.id
self.servers_mock.list.assert_called_with(**self.kwargs)
self.kwargs['image'] = self.image.id
self.sdk_client.servers.assert_called_with(**self.kwargs)
self.images_mock.assert_not_called()
self.flavors_mock.list.assert_called_once()
self.sdk_client.flavors.assert_called_once()
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, tuple(data))
@ -4767,12 +4776,13 @@ class TestServerList(_TestServerList):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.flavors_mock.get.assert_has_calls([mock.call(self.flavor.id)])
self.sdk_client.find_flavor.assert_has_calls(
[mock.call(self.flavor.id)])
self.search_opts['flavor'] = self.flavor.id
self.servers_mock.list.assert_called_with(**self.kwargs)
self.kwargs['flavor'] = self.flavor.id
self.sdk_client.servers.assert_called_with(**self.kwargs)
self.images_mock.assert_called_once()
self.flavors_mock.list.assert_not_called()
self.sdk_client.flavors.assert_not_called()
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, tuple(data))
@ -4791,9 +4801,9 @@ class TestServerList(_TestServerList):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.search_opts['changes-since'] = '2016-03-04T06:27:59Z'
self.search_opts['deleted'] = True
self.servers_mock.list.assert_called_with(**self.kwargs)
self.kwargs['changes-since'] = '2016-03-04T06:27:59Z'
self.kwargs['deleted'] = True
self.sdk_client.servers.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, tuple(data))
@ -4820,8 +4830,7 @@ class TestServerList(_TestServerList):
)
def test_server_list_with_tag(self):
self.app.client_manager.compute.api_version = api_versions.APIVersion(
'2.26')
self._set_mock_microversion('2.26')
arglist = [
'--tag', 'tag1',
@ -4834,16 +4843,15 @@ class TestServerList(_TestServerList):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.search_opts['tags'] = 'tag1,tag2'
self.kwargs['tags'] = 'tag1,tag2'
self.servers_mock.list.assert_called_with(**self.kwargs)
self.sdk_client.servers.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, tuple(data))
def test_server_list_with_tag_pre_v225(self):
self.app.client_manager.compute.api_version = api_versions.APIVersion(
'2.25')
self._set_mock_microversion('2.25')
arglist = [
'--tag', 'tag1',
@ -4863,9 +4871,7 @@ class TestServerList(_TestServerList):
str(ex))
def test_server_list_with_not_tag(self):
self.app.client_manager.compute.api_version = api_versions.APIVersion(
'2.26')
self._set_mock_microversion('2.26')
arglist = [
'--not-tag', 'tag1',
'--not-tag', 'tag2',
@ -4877,16 +4883,15 @@ class TestServerList(_TestServerList):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.search_opts['not-tags'] = 'tag1,tag2'
self.kwargs['not-tags'] = 'tag1,tag2'
self.servers_mock.list.assert_called_with(**self.kwargs)
self.sdk_client.servers.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, tuple(data))
def test_server_list_with_not_tag_pre_v226(self):
self.app.client_manager.compute.api_version = api_versions.APIVersion(
'2.25')
self._set_mock_microversion('2.25')
arglist = [
'--not-tag', 'tag1',
@ -4916,8 +4921,8 @@ class TestServerList(_TestServerList):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.search_opts['availability_zone'] = 'test-az'
self.servers_mock.list.assert_called_with(**self.kwargs)
self.kwargs['availability_zone'] = 'test-az'
self.sdk_client.servers.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data), tuple(data))
@ -4932,8 +4937,8 @@ class TestServerList(_TestServerList):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.search_opts['key_name'] = 'test-key'
self.servers_mock.list.assert_called_with(**self.kwargs)
self.kwargs['key_name'] = 'test-key'
self.sdk_client.servers.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data), tuple(data))
@ -4948,8 +4953,8 @@ class TestServerList(_TestServerList):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.search_opts['config_drive'] = True
self.servers_mock.list.assert_called_with(**self.kwargs)
self.kwargs['config_drive'] = True
self.sdk_client.servers.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data), tuple(data))
@ -4964,8 +4969,8 @@ class TestServerList(_TestServerList):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.search_opts['config_drive'] = False
self.servers_mock.list.assert_called_with(**self.kwargs)
self.kwargs['config_drive'] = False
self.sdk_client.servers.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data), tuple(data))
@ -4980,8 +4985,8 @@ class TestServerList(_TestServerList):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.search_opts['progress'] = '100'
self.servers_mock.list.assert_called_with(**self.kwargs)
self.kwargs['progress'] = '100'
self.sdk_client.servers.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data), tuple(data))
@ -5005,8 +5010,8 @@ class TestServerList(_TestServerList):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.search_opts['vm_state'] = 'active'
self.servers_mock.list.assert_called_with(**self.kwargs)
self.kwargs['vm_state'] = 'active'
self.sdk_client.servers.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data), tuple(data))
@ -5021,8 +5026,8 @@ class TestServerList(_TestServerList):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.search_opts['task_state'] = 'deleting'
self.servers_mock.list.assert_called_with(**self.kwargs)
self.kwargs['task_state'] = 'deleting'
self.sdk_client.servers.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data), tuple(data))
@ -5037,33 +5042,31 @@ class TestServerList(_TestServerList):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.search_opts['power_state'] = 1
self.servers_mock.list.assert_called_with(**self.kwargs)
self.kwargs['power_state'] = 1
self.sdk_client.servers.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns)
self.assertEqual(tuple(self.data), tuple(data))
def test_server_list_long_with_host_status_v216(self):
self.app.client_manager.compute.api_version = \
api_versions.APIVersion('2.16')
self._set_mock_microversion('2.16')
self.data1 = tuple(
(
s.id,
s.name,
s.status,
getattr(s, 'OS-EXT-STS:task_state'),
getattr(s, 'task_state'),
server.PowerStateColumn(
getattr(s, 'OS-EXT-STS:power_state')
getattr(s, 'power_state')
),
format_columns.DictListColumn(s.networks),
server.AddressesColumn(s.addresses),
# Image will be an empty string if boot-from-volume
self.image.name if s.image else server.IMAGE_STRING_FOR_BFV,
s.image['id'] if s.image else server.IMAGE_STRING_FOR_BFV,
self.flavor.name,
s.flavor['id'],
getattr(s, 'OS-EXT-AZ:availability_zone'),
getattr(s, 'OS-EXT-SRV-ATTR:host'),
s.Metadata,
getattr(s, 'availability_zone'),
server.HostColumn(getattr(s, 'hypervisor_hostname')),
format_columns.DictColumn(s.metadata),
) for s in self.servers)
arglist = [
@ -5078,18 +5081,18 @@ class TestServerList(_TestServerList):
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.sdk_client.servers.assert_called_with(**self.kwargs)
self.assertEqual(self.columns_long, columns)
self.assertEqual(tuple(self.data1), tuple(data))
# Next test with host_status in the data -- the column should be
# present in this case.
self.servers_mock.reset_mock()
self.sdk_client.servers.reset_mock()
self.attrs['host_status'] = 'UP'
servers = self.setup_servers_mock(3)
self.servers_mock.list.return_value = servers
servers = self.setup_sdk_servers_mock(3)
self.sdk_client.servers.return_value = servers
# Make sure the returned image and flavor IDs match the servers.
Image = collections.namedtuple('Image', 'id name')
@ -5099,12 +5102,6 @@ class TestServerList(_TestServerList):
for s in servers if s.image
]
Flavor = collections.namedtuple('Flavor', 'id name')
self.flavors_mock.list.return_value = [
Flavor(id=s.flavor['id'], name=self.flavor.name)
for s in servers
]
# Add the expected host_status column and data.
columns_long = self.columns_long + ('Host Status',)
self.data2 = tuple(
@ -5112,25 +5109,25 @@ class TestServerList(_TestServerList):
s.id,
s.name,
s.status,
getattr(s, 'OS-EXT-STS:task_state'),
getattr(s, 'task_state'),
server.PowerStateColumn(
getattr(s, 'OS-EXT-STS:power_state')
getattr(s, 'power_state')
),
format_columns.DictListColumn(s.networks),
server.AddressesColumn(s.addresses),
# Image will be an empty string if boot-from-volume
self.image.name if s.image else server.IMAGE_STRING_FOR_BFV,
s.image['id'] if s.image else server.IMAGE_STRING_FOR_BFV,
self.flavor.name,
s.flavor['id'],
getattr(s, 'OS-EXT-AZ:availability_zone'),
getattr(s, 'OS-EXT-SRV-ATTR:host'),
s.Metadata,
getattr(s, 'availability_zone'),
server.HostColumn(getattr(s, 'hypervisor_hostname')),
format_columns.DictColumn(s.metadata),
s.host_status,
) for s in servers)
columns, data = self.cmd.take_action(parsed_args)
self.servers_mock.list.assert_called_with(**self.kwargs)
self.sdk_client.servers.assert_called_with(**self.kwargs)
self.assertEqual(columns_long, columns)
self.assertEqual(tuple(self.data2), tuple(data))
@ -5178,8 +5175,8 @@ class TestServerListV273(_TestServerList):
}
# The servers to be listed.
self.servers = self.setup_servers_mock(3)
self.servers_mock.list.return_value = self.servers
self.servers = self.setup_sdk_servers_mock(3)
self.sdk_client.servers.return_value = self.servers
Image = collections.namedtuple('Image', 'id name')
self.images_mock.return_value = [
@ -5190,14 +5187,14 @@ class TestServerListV273(_TestServerList):
# The flavor information is embedded, so now reason for this to be
# called
self.flavors_mock.list = mock.NonCallableMock()
self.sdk_client.flavors = mock.NonCallableMock()
self.data = tuple(
(
s.id,
s.name,
s.status,
format_columns.DictListColumn(s.networks),
server.AddressesColumn(s.addresses),
# Image will be an empty string if boot-from-volume
self.image.name if s.image else server.IMAGE_STRING_FOR_BFV,
self.flavor.name,
@ -5221,8 +5218,7 @@ class TestServerListV273(_TestServerList):
def test_server_list_with_locked(self):
self.app.client_manager.compute.api_version = \
api_versions.APIVersion('2.73')
self._set_mock_microversion('2.73')
arglist = [
'--locked'
]
@ -5233,16 +5229,15 @@ class TestServerListV273(_TestServerList):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.search_opts['locked'] = True
self.servers_mock.list.assert_called_with(**self.kwargs)
self.kwargs['locked'] = True
self.sdk_client.servers.assert_called_with(**self.kwargs)
self.assertCountEqual(self.columns, columns)
self.assertCountEqual(self.data, tuple(data))
def test_server_list_with_unlocked_v273(self):
self._set_mock_microversion('2.73')
self.app.client_manager.compute.api_version = \
api_versions.APIVersion('2.73')
arglist = [
'--unlocked'
]
@ -5253,16 +5248,15 @@ class TestServerListV273(_TestServerList):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.search_opts['locked'] = False
self.servers_mock.list.assert_called_with(**self.kwargs)
self.kwargs['locked'] = False
self.sdk_client.servers.assert_called_with(**self.kwargs)
self.assertCountEqual(self.columns, columns)
self.assertCountEqual(self.data, tuple(data))
def test_server_list_with_locked_and_unlocked(self):
self.app.client_manager.compute.api_version = \
api_versions.APIVersion('2.73')
self._set_mock_microversion('2.73')
arglist = [
'--locked',
'--unlocked'
@ -5278,8 +5272,7 @@ class TestServerListV273(_TestServerList):
self.assertIn('Argument parse failed', str(ex))
def test_server_list_with_changes_before(self):
self.app.client_manager.compute.api_version = (
api_versions.APIVersion('2.66'))
self._set_mock_microversion('2.66')
arglist = [
'--changes-before', '2016-03-05T06:27:59Z',
'--deleted'
@ -5292,10 +5285,10 @@ class TestServerListV273(_TestServerList):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.search_opts['changes-before'] = '2016-03-05T06:27:59Z'
self.search_opts['deleted'] = True
self.kwargs['changes-before'] = '2016-03-05T06:27:59Z'
self.kwargs['deleted'] = True
self.servers_mock.list.assert_called_with(**self.kwargs)
self.sdk_client.servers.assert_called_with(**self.kwargs)
self.assertCountEqual(self.columns, columns)
self.assertCountEqual(self.data, tuple(data))
@ -5303,9 +5296,7 @@ class TestServerListV273(_TestServerList):
@mock.patch.object(iso8601, 'parse_date', side_effect=iso8601.ParseError)
def test_server_list_with_invalid_changes_before(
self, mock_parse_isotime):
self.app.client_manager.compute.api_version = (
api_versions.APIVersion('2.66'))
self._set_mock_microversion('2.66')
arglist = [
'--changes-before', 'Invalid time value',
]
@ -5325,8 +5316,7 @@ class TestServerListV273(_TestServerList):
)
def test_server_with_changes_before_pre_v266(self):
self.app.client_manager.compute.api_version = (
api_versions.APIVersion('2.65'))
self._set_mock_microversion('2.65')
arglist = [
'--changes-before', '2016-03-05T06:27:59Z',
@ -5344,8 +5334,7 @@ class TestServerListV273(_TestServerList):
parsed_args)
def test_server_list_v269_with_partial_constructs(self):
self.app.client_manager.compute.api_version = \
api_versions.APIVersion('2.69')
self._set_mock_microversion('2.69')
arglist = []
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@ -5371,10 +5360,10 @@ class TestServerListV273(_TestServerList):
# it will fail at formatting the networks info later on.
"networks": {}
}
server = compute_fakes.fakes.FakeResource(
fake_server = compute_fakes.fakes.FakeResource(
info=server_dict,
)
self.servers.append(server)
self.servers.append(fake_server)
columns, data = self.cmd.take_action(parsed_args)
# get the first three servers out since our interest is in the partial
# server.
@ -5384,7 +5373,7 @@ class TestServerListV273(_TestServerList):
partial_server = next(data)
expected_row = (
'server-id-95a56bfc4xxxxxx28d7e418bfd97813a', '',
'UNKNOWN', format_columns.DictListColumn({}), '', '')
'UNKNOWN', server.AddressesColumn(''), '', '')
self.assertEqual(expected_row, partial_server)

View File

@ -0,0 +1,5 @@
---
features:
- |
The ``server list`` command now uses the OpenStack SDK instead of the
Python nova bindings.