From 93578ef85ba5f94ec5dc8fc4b2917e91c74121da Mon Sep 17 00:00:00 2001 From: melanie witt Date: Thu, 18 Nov 2021 00:31:02 +0000 Subject: Add 'Host Status' to 'server list --long' with >= v2.16 Currently, the 'Host Status' field is shown only for 'server show' but not for 'server list'. The host_status can be helpful for users who are having issues with servers that show a status of ACTIVE, as it can show a hint about the compute host status when nova policy is configured to allow it. Story: 2009689 Task: 44003 Change-Id: I6209cf52044218b7b32ab2fa5712574f12ba2f5f --- openstackclient/compute/v2/server.py | 15 ++++ .../tests/unit/compute/v2/test_server.py | 93 ++++++++++++++++++++++ 2 files changed, 108 insertions(+) (limited to 'openstackclient') diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 291244e7..436c2597 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -2569,6 +2569,21 @@ class ListServer(command.Lister): else: s.security_groups_name = [] + # The host_status field contains the status of the compute host the + # server is on. It is only returned by the API when the nova-api + # policy allows. Users can look at the host_status field when, for + # example, their server has status ACTIVE but is unresponsive. The + # host_status field can indicate a possible problem on the host + # it's on, providing useful information to a user in this + # situation. + if ( + compute_client.api_version >= api_versions.APIVersion('2.16') and + parsed_args.long + ): + if any([hasattr(s, 'host_status') for s in data]): + columns += ('Host Status',) + column_headers += ('Host Status',) + table = ( column_headers, ( diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index bd0b31bf..19e90a43 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -4885,6 +4885,99 @@ class TestServerList(_TestServerList): 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.data1 = tuple( + ( + s.id, + s.name, + s.status, + getattr(s, 'OS-EXT-STS:task_state'), + server.PowerStateColumn( + getattr(s, 'OS-EXT-STS:power_state') + ), + format_columns.DictListColumn(s.networks), + # 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, + ) for s in self.servers) + + arglist = [ + '--long' + ] + verifylist = [ + ('long', True), + ] + + # First test without host_status in the data -- the column should not + # be present in this case. + 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.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.attrs['host_status'] = 'UP' + servers = self.setup_servers_mock(3) + self.servers_mock.list.return_value = servers + + # Make sure the returned image and flavor IDs match the servers. + Image = collections.namedtuple('Image', 'id name') + self.images_mock.return_value = [ + Image(id=s.image['id'], name=self.image.name) + # Image will be an empty string if boot-from-volume + 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( + ( + s.id, + s.name, + s.status, + getattr(s, 'OS-EXT-STS:task_state'), + server.PowerStateColumn( + getattr(s, 'OS-EXT-STS:power_state') + ), + format_columns.DictListColumn(s.networks), + # 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, + 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.assertEqual(columns_long, columns) + self.assertEqual(tuple(self.data2), tuple(data)) + class TestServerListV273(_TestServerList): -- cgit v1.2.1