summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Wilson <danielcw@bu.edu>2022-10-11 00:39:39 -0400
committerDaniel Wilson <danielcw@bu.edu>2022-11-29 20:24:45 -0500
commitc97f73ce2a897fe6b83f08de1ce6e1865e9a83c5 (patch)
tree93f0271e3747d47cb487963fb65a2ac00679778b
parent56b0f6de0e950cf23aeb25ff234b307e3857dcfa (diff)
downloadpython-openstackclient-c97f73ce2a897fe6b83f08de1ce6e1865e9a83c5.tar.gz
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
-rw-r--r--openstackclient/compute/v2/server.py126
-rw-r--r--openstackclient/tests/functional/compute/v2/test_server.py2
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server.py261
-rw-r--r--releasenotes/notes/nova-gaps-server-list-to-sdk-88c8bfc10a9e3032.yaml5
4 files changed, 205 insertions, 189 deletions
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index 609faf5a..f3315b9d 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -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
),
diff --git a/openstackclient/tests/functional/compute/v2/test_server.py b/openstackclient/tests/functional/compute/v2/test_server.py
index 0558ef62..8c62fa9d 100644
--- a/openstackclient/tests/functional/compute/v2/test_server.py
+++ b/openstackclient/tests/functional/compute/v2/test_server.py
@@ -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):
diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py
index f59c954a..45aff6a3 100644
--- a/openstackclient/tests/unit/compute/v2/test_server.py
+++ b/openstackclient/tests/unit/compute/v2/test_server.py
@@ -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)
diff --git a/releasenotes/notes/nova-gaps-server-list-to-sdk-88c8bfc10a9e3032.yaml b/releasenotes/notes/nova-gaps-server-list-to-sdk-88c8bfc10a9e3032.yaml
new file mode 100644
index 00000000..01d690e3
--- /dev/null
+++ b/releasenotes/notes/nova-gaps-server-list-to-sdk-88c8bfc10a9e3032.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - |
+ The ``server list`` command now uses the OpenStack SDK instead of the
+ Python nova bindings.