Skip to content

Commit a103b6c

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Use the SDK for server show"
2 parents 3f7663a + 70dbb01 commit a103b6c

4 files changed

Lines changed: 109 additions & 26 deletions

File tree

openstackclient/compute/v2/server.py

Lines changed: 69 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ def human_readable(self):
7777
except Exception:
7878
return 'N/A'
7979

80+
def machine_readable(self):
81+
return {k: [i['addr'] for i in v if 'addr' in i]
82+
for k, v in self._value.items()}
83+
8084

8185
class HostColumn(cliff_columns.FormattableColumn):
8286
"""Generate a formatted string of a hostname."""
@@ -133,14 +137,61 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True):
133137
the latest details of a server after creating it.
134138
:rtype: a dict of server details
135139
"""
140+
# Note: Some callers of this routine pass a novaclient server, and others
141+
# pass an SDK server. Column names may be different across those cases.
136142
info = server.to_dict()
137143
if refresh:
138144
server = utils.find_resource(compute_client.servers, info['id'])
139145
info.update(server.to_dict())
140146

147+
# Some commands using this routine were originally implemented with the
148+
# nova python wrappers, and were later migrated to use the SDK. Map the
149+
# SDK's property names to the original property names to maintain backward
150+
# compatibility for existing users. Data is duplicated under both the old
151+
# and new name so users can consume the data by either name.
152+
column_map = {
153+
'access_ipv4': 'accessIPv4',
154+
'access_ipv6': 'accessIPv6',
155+
'admin_password': 'adminPass',
156+
'admin_password': 'adminPass',
157+
'volumes': 'os-extended-volumes:volumes_attached',
158+
'availability_zone': 'OS-EXT-AZ:availability_zone',
159+
'block_device_mapping': 'block_device_mapping_v2',
160+
'compute_host': 'OS-EXT-SRV-ATTR:host',
161+
'created_at': 'created',
162+
'disk_config': 'OS-DCF:diskConfig',
163+
'flavor_id': 'flavorRef',
164+
'has_config_drive': 'config_drive',
165+
'host_id': 'hostId',
166+
'fault': 'fault',
167+
'hostname': 'OS-EXT-SRV-ATTR:hostname',
168+
'hypervisor_hostname': 'OS-EXT-SRV-ATTR:hypervisor_hostname',
169+
'image_id': 'imageRef',
170+
'instance_name': 'OS-EXT-SRV-ATTR:instance_name',
171+
'is_locked': 'locked',
172+
'kernel_id': 'OS-EXT-SRV-ATTR:kernel_id',
173+
'launch_index': 'OS-EXT-SRV-ATTR:launch_index',
174+
'launched_at': 'OS-SRV-USG:launched_at',
175+
'power_state': 'OS-EXT-STS:power_state',
176+
'project_id': 'tenant_id',
177+
'ramdisk_id': 'OS-EXT-SRV-ATTR:ramdisk_id',
178+
'reservation_id': 'OS-EXT-SRV-ATTR:reservation_id',
179+
'root_device_name': 'OS-EXT-SRV-ATTR:root_device_name',
180+
'scheduler_hints': 'OS-SCH-HNT:scheduler_hints',
181+
'task_state': 'OS-EXT-STS:task_state',
182+
'terminated_at': 'OS-SRV-USG:terminated_at',
183+
'updated_at': 'updated',
184+
'user_data': 'OS-EXT-SRV-ATTR:user_data',
185+
'vm_state': 'OS-EXT-STS:vm_state',
186+
}
187+
188+
info.update({
189+
column_map[column]: data for column, data in info.items()
190+
if column in column_map})
191+
141192
# Convert the image blob to a name
142193
image_info = info.get('image', {})
143-
if image_info:
194+
if image_info and any(image_info.values()):
144195
image_id = image_info.get('id', '')
145196
try:
146197
image = image_client.get_image(image_id)
@@ -188,7 +239,9 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True):
188239

189240
# NOTE(dtroyer): novaclient splits these into separate entries...
190241
# Format addresses in a useful way
191-
info['addresses'] = format_columns.DictListColumn(server.networks)
242+
info['addresses'] = (
243+
AddressesColumn(info['addresses']) if 'addresses' in info
244+
else format_columns.DictListColumn(info.get('networks')))
192245

193246
# Map 'metadata' field to 'properties'
194247
info['properties'] = format_columns.DictColumn(info.pop('metadata'))
@@ -4327,32 +4380,34 @@ def get_parser(self, prog_name):
43274380
return parser
43284381

43294382
def take_action(self, parsed_args):
4330-
compute_client = self.app.client_manager.compute
4331-
server = utils.find_resource(
4332-
compute_client.servers, parsed_args.server)
4383+
compute_client = self.app.client_manager.sdk_connection.compute
4384+
4385+
# Find by name or ID, then get the full details of the server
4386+
server = compute_client.find_server(
4387+
parsed_args.server, ignore_missing=False)
4388+
server = compute_client.get_server(server)
43334389

43344390
if parsed_args.diagnostics:
4335-
(resp, data) = server.diagnostics()
4336-
if not resp.status_code == 200:
4337-
self.app.stderr.write(_(
4338-
"Error retrieving diagnostics data\n"
4339-
))
4340-
return ({}, {})
4391+
data = compute_client.get_server_diagnostics(server)
43414392
return zip(*sorted(data.items()))
43424393

43434394
topology = None
43444395
if parsed_args.topology:
4345-
if compute_client.api_version < api_versions.APIVersion('2.78'):
4396+
if not sdk_utils.supports_microversion(compute_client, '2.78'):
43464397
msg = _(
43474398
'--os-compute-api-version 2.78 or greater is required to '
43484399
'support the --topology option'
43494400
)
43504401
raise exceptions.CommandError(msg)
43514402

4352-
topology = server.topology()
4403+
topology = server.fetch_topology(compute_client)
43534404

43544405
data = _prep_server_detail(
4355-
compute_client, self.app.client_manager.image, server,
4406+
# TODO(dannosliwcd): Replace these clients with SDK clients after
4407+
# all callers of _prep_server_detail() are using the SDK.
4408+
self.app.client_manager.compute,
4409+
self.app.client_manager.image,
4410+
server,
43564411
refresh=False)
43574412

43584413
if topology:

openstackclient/tests/functional/compute/v2/test_server.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
# under the License.
1212

1313
import itertools
14+
import json
1415
import time
1516
import uuid
1617

@@ -288,6 +289,33 @@ def test_server_set(self):
288289
)
289290
self.assertOutput("", raw_output)
290291

292+
def test_server_show(self):
293+
"""Test server show"""
294+
cmd_output = self.server_create()
295+
name = cmd_output['name']
296+
297+
# Simple show
298+
cmd_output = json.loads(self.openstack(
299+
f'server show -f json {name}'
300+
))
301+
self.assertEqual(
302+
name,
303+
cmd_output["name"],
304+
)
305+
306+
# Show diagnostics
307+
cmd_output = json.loads(self.openstack(
308+
f'server show -f json {name} --diagnostics'
309+
))
310+
self.assertIn('driver', cmd_output)
311+
312+
# Show topology
313+
cmd_output = json.loads(self.openstack(
314+
f'server show -f json {name} --topology '
315+
f'--os-compute-api-version 2.78'
316+
))
317+
self.assertIn('topology', cmd_output)
318+
291319
def test_server_actions(self):
292320
"""Test server action pairs
293321

openstackclient/tests/unit/compute/v2/test_server.py

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7931,20 +7931,15 @@ def setUp(self):
79317931
'tenant_id': 'tenant-id-xxx',
79327932
'networks': {'public': ['10.20.30.40', '2001:db8::f']},
79337933
}
7934-
# Fake the server.diagnostics() method. The return value contains http
7935-
# response and data. The data is a dict. Sincce this method itself is
7936-
# faked, we don't need to fake everything of the return value exactly.
7937-
resp = mock.Mock()
7938-
resp.status_code = 200
7934+
self.sdk_client.get_server_diagnostics.return_value = {'test': 'test'}
79397935
server_method = {
7940-
'diagnostics': (resp, {'test': 'test'}),
7941-
'topology': self.topology,
7936+
'fetch_topology': self.topology,
79427937
}
79437938
self.server = compute_fakes.FakeServer.create_one_server(
79447939
attrs=server_info, methods=server_method)
79457940

79467941
# This is the return value for utils.find_resource()
7947-
self.servers_mock.get.return_value = self.server
7942+
self.sdk_client.get_server.return_value = self.server
79487943
self.get_image_mock.return_value = self.image
79497944
self.flavors_mock.get.return_value = self.flavor
79507945

@@ -8045,8 +8040,7 @@ def test_show_diagnostics(self):
80458040
self.assertEqual(('test',), data)
80468041

80478042
def test_show_topology(self):
8048-
self.app.client_manager.compute.api_version = \
8049-
api_versions.APIVersion('2.78')
8043+
self._set_mock_microversion('2.78')
80508044

80518045
arglist = [
80528046
'--topology',
@@ -8068,8 +8062,7 @@ def test_show_topology(self):
80688062
self.assertCountEqual(self.data, data)
80698063

80708064
def test_show_topology_pre_v278(self):
8071-
self.app.client_manager.compute.api_version = \
8072-
api_versions.APIVersion('2.77')
8065+
self._set_mock_microversion('2.77')
80738066

80748067
arglist = [
80758068
'--topology',
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
features:
3+
- |
4+
The ``server show`` command now uses the OpenStack SDK instead of the
5+
Python nova bindings. The command prints data fields both by their
6+
novaclient names used in previous releases as well as the names used in the
7+
SDK.

0 commit comments

Comments
 (0)