Fix microversion 2.96

This change fixes missing conditional logic for
microversion 2.96 which adds `pinned_availability_zone`
field to `openstack server list` output.

Change-Id: I1e398bb3379fa6443b0a44db76baaf6241a945e7
Signed-off-by: Rajesh Tailor <ratailor@redhat.com>
This commit is contained in:
Rajesh Tailor
2025-06-23 13:17:30 +05:30
parent 34f431bade
commit dbddbf9760
2 changed files with 168 additions and 20 deletions

View File

@@ -183,9 +183,13 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True):
'updated_at': 'updated', 'updated_at': 'updated',
'user_data': 'OS-EXT-SRV-ATTR:user_data', 'user_data': 'OS-EXT-SRV-ATTR:user_data',
'vm_state': 'OS-EXT-STS:vm_state', 'vm_state': 'OS-EXT-STS:vm_state',
'pinned_availability_zone': 'pinned_availability_zone',
'scheduler_hints': 'scheduler_hints', 'scheduler_hints': 'scheduler_hints',
} }
# NOTE(ratailor): microversion 2.96 introduces
# pinned_availability_zone support
if sdk_utils.supports_microversion(compute_client, '2.96'):
column_map['pinned_availability_zone'] = 'pinned_availability_zone'
# Some columns returned by openstacksdk should not be shown because they're # Some columns returned by openstacksdk should not be shown because they're
# either irrelevant or duplicates # either irrelevant or duplicates
ignored_columns = { ignored_columns = {
@@ -240,6 +244,11 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True):
if not sdk_utils.supports_microversion(compute_client, '2.100'): if not sdk_utils.supports_microversion(compute_client, '2.100'):
info.pop('scheduler_hints', None) info.pop('scheduler_hints', None)
# NOTE(ratailor): microversion 2.96 introduces
# pinned_availability_zone support
if not sdk_utils.supports_microversion(compute_client, '2.96'):
info.pop('pinned_availability_zone', None)
# Convert the image blob to a name # Convert the image blob to a name
image_info = info.get('image', {}) image_info = info.get('image', {})
if image_info and any(image_info.values()): if image_info and any(image_info.values()):
@@ -2838,18 +2847,19 @@ class ListServer(command.Lister):
if parsed_args.long: if parsed_args.long:
columns += ( columns += (
'availability_zone', 'availability_zone',
'pinned_availability_zone',
'hypervisor_hostname', 'hypervisor_hostname',
'metadata', 'metadata',
'scheduler_hints', 'scheduler_hints',
) )
column_headers += ( column_headers += (
'Availability Zone', 'Availability Zone',
'Pinned Availability Zone',
'Host', 'Host',
'Properties', 'Properties',
'Scheduler Hints', 'Scheduler Hints',
) )
if sdk_utils.supports_microversion(compute_client, '2.96'):
columns += ('pinned_availability_zone',)
column_headers += ('Pinned Availability Zone',)
if parsed_args.all_projects: if parsed_args.all_projects:
columns += ('project_id',) columns += ('project_id',)
@@ -2887,10 +2897,11 @@ class ListServer(command.Lister):
column_headers += ('Availability Zone',) column_headers += ('Availability Zone',)
if c in ( if c in (
'pinned_availability_zone', 'pinned_availability_zone',
"Pinned Availability Zone", 'Pinned Availability Zone',
): ):
columns += ('Pinned Availability Zone',) if sdk_utils.supports_microversion(compute_client, '2.96'):
column_headers += ('Pinned Availability Zone',) columns += ('pinned_availability_zone',)
column_headers += ('Pinned Availability Zone',)
if c in ('Host', "host"): if c in ('Host', "host"):
columns += ('hypervisor_hostname',) columns += ('hypervisor_hostname',)
column_headers += ('Host',) column_headers += ('Host',)

View File

@@ -1212,7 +1212,6 @@ class TestServerCreate(TestServer):
'locked', 'locked',
'locked_reason', 'locked_reason',
'name', 'name',
'pinned_availability_zone',
'progress', 'progress',
'project_id', 'project_id',
'properties', 'properties',
@@ -1261,7 +1260,6 @@ class TestServerCreate(TestServer):
None, # locked None, # locked
None, # locked_reason None, # locked_reason
self.server.name, self.server.name,
None, # pinned_availability_zone
None, # progress None, # progress
None, # project_id None, # project_id
format_columns.DictColumn({}), # properties format_columns.DictColumn({}), # properties
@@ -4583,7 +4581,6 @@ class _TestServerList(TestServer):
'Flavor Name', 'Flavor Name',
'Flavor ID', 'Flavor ID',
'Availability Zone', 'Availability Zone',
'Pinned Availability Zone',
'Host', 'Host',
'Properties', 'Properties',
'Scheduler Hints', 'Scheduler Hints',
@@ -4732,7 +4729,6 @@ class TestServerList(_TestServerList):
self.flavor.name, self.flavor.name,
s.flavor['id'], s.flavor['id'],
getattr(s, 'availability_zone'), getattr(s, 'availability_zone'),
getattr(s, 'pinned_availability_zone', ''),
server.HostColumn(getattr(s, 'hypervisor_hostname')), server.HostColumn(getattr(s, 'hypervisor_hostname')),
format_columns.DictColumn(s.metadata), format_columns.DictColumn(s.metadata),
format_columns.DictListColumn(None), format_columns.DictListColumn(None),
@@ -4809,8 +4805,6 @@ class TestServerList(_TestServerList):
'-c', '-c',
'Availability Zone', 'Availability Zone',
'-c', '-c',
'Pinned Availability Zone',
'-c',
'Host', 'Host',
'-c', '-c',
'Properties', 'Properties',
@@ -4835,7 +4829,6 @@ class TestServerList(_TestServerList):
self.assertIn('Image ID', columns) self.assertIn('Image ID', columns)
self.assertIn('Flavor ID', columns) self.assertIn('Flavor ID', columns)
self.assertIn('Availability Zone', columns) self.assertIn('Availability Zone', columns)
self.assertIn('Pinned Availability Zone', columns)
self.assertIn('Host', columns) self.assertIn('Host', columns)
self.assertIn('Properties', columns) self.assertIn('Properties', columns)
self.assertIn('Scheduler Hints', columns) self.assertIn('Scheduler Hints', columns)
@@ -5249,7 +5242,6 @@ class TestServerList(_TestServerList):
self.flavor.name, self.flavor.name,
s.flavor['id'], s.flavor['id'],
getattr(s, 'availability_zone'), getattr(s, 'availability_zone'),
getattr(s, 'pinned_availability_zone', ''),
server.HostColumn(getattr(s, 'hypervisor_hostname')), server.HostColumn(getattr(s, 'hypervisor_hostname')),
format_columns.DictColumn(s.metadata), format_columns.DictColumn(s.metadata),
format_columns.DictListColumn(s.scheduler_hints), format_columns.DictListColumn(s.scheduler_hints),
@@ -5305,7 +5297,6 @@ class TestServerList(_TestServerList):
self.flavor.name, self.flavor.name,
s.flavor['id'], s.flavor['id'],
getattr(s, 'availability_zone'), getattr(s, 'availability_zone'),
getattr(s, 'pinned_availability_zone', ''),
server.HostColumn(getattr(s, 'hypervisor_hostname')), server.HostColumn(getattr(s, 'hypervisor_hostname')),
format_columns.DictColumn(s.metadata), format_columns.DictColumn(s.metadata),
format_columns.DictListColumn(s.scheduler_hints), format_columns.DictListColumn(s.scheduler_hints),
@@ -5343,7 +5334,6 @@ class TestServerListV273(_TestServerList):
'Image ID', 'Image ID',
'Flavor', 'Flavor',
'Availability Zone', 'Availability Zone',
'Pinned Availability Zone',
'Host', 'Host',
'Properties', 'Properties',
'Scheduler Hints', 'Scheduler Hints',
@@ -5541,6 +5531,157 @@ class TestServerListV273(_TestServerList):
self.assertEqual(expected_row, partial_server) self.assertEqual(expected_row, partial_server)
class TestServerListV296(_TestServerList):
columns = (
'ID',
'Name',
'Status',
'Networks',
'Image',
'Flavor',
)
columns_long = (
'ID',
'Name',
'Status',
'Task State',
'Power State',
'Networks',
'Image Name',
'Image ID',
'Flavor',
'Availability Zone',
'Host',
'Properties',
'Scheduler Hints',
'Pinned Availability Zone',
)
def setUp(self):
super().setUp()
self.set_compute_api_version('2.96')
Image = collections.namedtuple('Image', 'id name')
self.image_client.images.return_value = [
Image(id=s.image['id'], name=self.image.name)
# Image will be an empty string if boot-from-volume
for s in self.servers
if s.image
]
Flavor = collections.namedtuple('Flavor', 'id name')
self.compute_client.flavors.return_value = [
Flavor(id=s.flavor['id'], name=self.flavor.name)
for s in self.servers
]
self.data = tuple(
(
s.id,
s.name,
s.status,
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,
)
for s in self.servers
)
def test_server_list_long_option(self):
self.data = tuple(
(
s.id,
s.name,
s.status,
getattr(s, 'task_state'),
server.PowerStateColumn(getattr(s, 'power_state')),
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,
getattr(s, 'availability_zone'),
server.HostColumn(getattr(s, 'hypervisor_hostname')),
format_columns.DictColumn(s.metadata),
format_columns.DictListColumn(None),
getattr(s, 'pinned_availability_zone', ''),
)
for s in self.servers
)
arglist = [
'--long',
]
verifylist = [
('all_projects', False),
('long', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.compute_client.servers.assert_called_with(**self.kwargs)
image_ids = {s.image['id'] for s in self.servers if s.image}
self.image_client.images.assert_called_once_with(
id=f'in:{",".join(image_ids)}',
)
self.compute_client.flavors.assert_called_once_with(is_public=None)
self.assertEqual(self.columns_long, columns)
self.assertEqual(self.data, tuple(data))
def test_server_list_column_option(self):
arglist = [
'-c',
'Project ID',
'-c',
'User ID',
'-c',
'Created At',
'-c',
'Security Groups',
'-c',
'Task State',
'-c',
'Power State',
'-c',
'Image ID',
'-c',
'Flavor ID',
'-c',
'Availability Zone',
'-c',
'Host',
'-c',
'Properties',
'-c',
'Scheduler Hints',
'-c',
'Pinned Availability Zone',
'--long',
]
verifylist = [
('long', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.compute_client.servers.assert_called_with(**self.kwargs)
self.assertIn('Project ID', columns)
self.assertIn('User ID', columns)
self.assertIn('Created At', columns)
self.assertIn('Security Groups', columns)
self.assertIn('Task State', columns)
self.assertIn('Power State', columns)
self.assertIn('Image ID', columns)
self.assertIn('Flavor ID', columns)
self.assertIn('Availability Zone', columns)
self.assertIn('Pinned Availability Zone', columns)
self.assertIn('Host', columns)
self.assertIn('Properties', columns)
self.assertIn('Scheduler Hints', columns)
self.assertCountEqual(columns, set(columns))
class TestServerAction(compute_fakes.TestComputev2): class TestServerAction(compute_fakes.TestComputev2):
def run_method_with_sdk_servers(self, method_name, server_count): def run_method_with_sdk_servers(self, method_name, server_count):
servers = compute_fakes.create_servers(count=server_count) servers = compute_fakes.create_servers(count=server_count)
@@ -8533,7 +8674,6 @@ class TestServerShow(TestServer):
'locked', 'locked',
'locked_reason', 'locked_reason',
'name', 'name',
'pinned_availability_zone',
'progress', 'progress',
'project_id', 'project_id',
'properties', 'properties',
@@ -8583,7 +8723,6 @@ class TestServerShow(TestServer):
None, # locked None, # locked
None, # locked_reason None, # locked_reason
self.server.name, self.server.name,
None, # pinned_availability_zone
None, # progress None, # progress
'tenant-id-xxx', # project_id 'tenant-id-xxx', # project_id
format_columns.DictColumn({}), # properties format_columns.DictColumn({}), # properties
@@ -9522,7 +9661,6 @@ class TestServerGeneral(TestServer):
'locked': None, 'locked': None,
'locked_reason': None, 'locked_reason': None,
'name': _server.name, 'name': _server.name,
'pinned_availability_zone': None,
'progress': None, 'progress': None,
'project_id': 'tenant-id-xxx', 'project_id': 'tenant-id-xxx',
'properties': format_columns.DictColumn({}), 'properties': format_columns.DictColumn({}),
@@ -9609,7 +9747,6 @@ class TestServerGeneral(TestServer):
'locked': None, 'locked': None,
'locked_reason': None, 'locked_reason': None,
'name': _server.name, 'name': _server.name,
'pinned_availability_zone': None,
'progress': None, 'progress': None,
'project_id': 'tenant-id-xxx', 'project_id': 'tenant-id-xxx',
'properties': format_columns.DictColumn({}), 'properties': format_columns.DictColumn({}),