Skip to content

Commit 50544ae

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "compute: Fix formatting of 'server show'"
2 parents 37b4201 + f43e2ed commit 50544ae

File tree

2 files changed

+76
-43
lines changed

2 files changed

+76
-43
lines changed

openstackclient/compute/v2/server.py

Lines changed: 71 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -149,16 +149,13 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True):
149149
# Some commands using this routine were originally implemented with the
150150
# nova python wrappers, and were later migrated to use the SDK. Map the
151151
# SDK's property names to the original property names to maintain backward
152-
# compatibility for existing users. Data is duplicated under both the old
153-
# and new name so users can consume the data by either name.
152+
# compatibility for existing users.
154153
column_map = {
155154
'access_ipv4': 'accessIPv4',
156155
'access_ipv6': 'accessIPv6',
157156
'admin_password': 'adminPass',
158-
'admin_password': 'adminPass',
159-
'volumes': 'os-extended-volumes:volumes_attached',
157+
'attached_volumes': 'volumes_attached',
160158
'availability_zone': 'OS-EXT-AZ:availability_zone',
161-
'block_device_mapping': 'block_device_mapping_v2',
162159
'compute_host': 'OS-EXT-SRV-ATTR:host',
163160
'created_at': 'created',
164161
'disk_config': 'OS-DCF:diskConfig',
@@ -168,7 +165,6 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True):
168165
'fault': 'fault',
169166
'hostname': 'OS-EXT-SRV-ATTR:hostname',
170167
'hypervisor_hostname': 'OS-EXT-SRV-ATTR:hypervisor_hostname',
171-
'image_id': 'imageRef',
172168
'instance_name': 'OS-EXT-SRV-ATTR:instance_name',
173169
'is_locked': 'locked',
174170
'kernel_id': 'OS-EXT-SRV-ATTR:kernel_id',
@@ -179,21 +175,56 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True):
179175
'ramdisk_id': 'OS-EXT-SRV-ATTR:ramdisk_id',
180176
'reservation_id': 'OS-EXT-SRV-ATTR:reservation_id',
181177
'root_device_name': 'OS-EXT-SRV-ATTR:root_device_name',
182-
'scheduler_hints': 'OS-SCH-HNT:scheduler_hints',
183178
'task_state': 'OS-EXT-STS:task_state',
184179
'terminated_at': 'OS-SRV-USG:terminated_at',
185180
'updated_at': 'updated',
186181
'user_data': 'OS-EXT-SRV-ATTR:user_data',
187182
'vm_state': 'OS-EXT-STS:vm_state',
188183
}
184+
# Some columns returned by openstacksdk should not be shown because they're
185+
# either irrelevant or duplicates
186+
ignored_columns = {
187+
# computed columns
188+
'interface_ip',
189+
'location',
190+
'private_v4',
191+
'private_v6',
192+
'public_v4',
193+
'public_v6',
194+
# create-only columns
195+
'block_device_mapping',
196+
'image_id',
197+
'max_count',
198+
'min_count',
199+
'scheduler_hints',
200+
# aliases
201+
'volumes',
202+
# unnecessary
203+
'links',
204+
}
205+
# Some columns are only present in certain responses and should not be
206+
# shown otherwise.
207+
optional_columns = {
208+
'admin_password', # removed in 2.14
209+
'fault', # only present in errored servers
210+
'flavor_id', # removed in 2.47
211+
'networks', # only present in create responses
212+
'security_groups', # only present in create, detail responses
213+
}
189214

190-
info.update(
191-
{
192-
column_map[column]: data
193-
for column, data in info.items()
194-
if column in column_map
195-
}
196-
)
215+
data = {}
216+
for key, value in info.items():
217+
if key in ignored_columns:
218+
continue
219+
220+
if key in optional_columns:
221+
if info[key] is None:
222+
continue
223+
224+
alias = column_map.get(key)
225+
data[alias or key] = value
226+
227+
info = data
197228

198229
# Convert the image blob to a name
199230
image_info = info.get('image', {})
@@ -214,46 +245,57 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True):
214245
# Convert the flavor blob to a name
215246
flavor_info = info.get('flavor', {})
216247
# Microversion 2.47 puts the embedded flavor into the server response
217-
# body but omits the id, so if not present we just expose the flavor
218-
# dict in the server output.
219-
if 'id' in flavor_info:
248+
# body. The presence of the 'original_name' attribute indicates this.
249+
if flavor_info.get('original_name') is None: # microversion < 2.47
220250
flavor_id = flavor_info.get('id', '')
221251
try:
222252
flavor = utils.find_resource(compute_client.flavors, flavor_id)
223253
info['flavor'] = "%s (%s)" % (flavor.name, flavor_id)
224254
except Exception:
225255
info['flavor'] = flavor_id
226-
else:
256+
else: # microversion >= 2.47
227257
info['flavor'] = format_columns.DictColumn(flavor_info)
228258

229-
if 'os-extended-volumes:volumes_attached' in info:
259+
# there's a lot of redundant information in BDMs - strip it
260+
if 'volumes_attached' in info:
230261
info.update(
231262
{
232263
'volumes_attached': format_columns.ListDictColumn(
233-
info.pop('os-extended-volumes:volumes_attached')
264+
[
265+
{
266+
k: v
267+
for k, v in volume.items()
268+
if v is not None and k != 'location'
269+
}
270+
for volume in info.pop('volumes_attached') or []
271+
]
234272
)
235273
}
236274
)
275+
237276
if 'security_groups' in info:
238277
info.update(
239278
{
240279
'security_groups': format_columns.ListDictColumn(
241-
info.pop('security_groups')
280+
info.pop('security_groups'),
242281
)
243282
}
244283
)
284+
245285
if 'tags' in info:
246286
info.update({'tags': format_columns.ListColumn(info.pop('tags'))})
247287

248-
# NOTE(dtroyer): novaclient splits these into separate entries...
249-
# Format addresses in a useful way
250-
info['addresses'] = (
251-
AddressesColumn(info['addresses'])
252-
if 'addresses' in info
253-
else format_columns.DictListColumn(info.get('networks'))
254-
)
288+
# Map 'networks' to 'addresses', if present. Note that the 'networks' key
289+
# is used for create responses, otherwise it's 'addresses'. We know it'll
290+
# be set because this is one of our optional columns.
291+
if 'networks' in info:
292+
info['addresses'] = format_columns.DictListColumn(
293+
info.pop('networks', {}),
294+
)
295+
else:
296+
info['addresses'] = AddressesColumn(info.get('addresses', {}))
255297

256-
# Map 'metadata' field to 'properties'
298+
# Map 'metadata' field to 'properties' and format
257299
info['properties'] = format_columns.DictColumn(info.pop('metadata'))
258300

259301
# Migrate tenant_id to project_id naming
@@ -266,9 +308,6 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True):
266308
info['OS-EXT-STS:power_state']
267309
)
268310

269-
# Remove values that are long and not too useful
270-
info.pop('links', None)
271-
272311
return info
273312

274313

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

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,7 +1313,6 @@ class TestServerCreate(TestServer):
13131313
'id',
13141314
'image',
13151315
'name',
1316-
'networks',
13171316
'properties',
13181317
)
13191318

@@ -1327,7 +1326,6 @@ def datalist(self):
13271326
self.new_server.id,
13281327
self.image.name + ' (' + self.new_server.image.get('id') + ')',
13291328
self.new_server.name,
1330-
self.new_server.networks,
13311329
format_columns.DictColumn(self.new_server.metadata),
13321330
)
13331331
return datalist
@@ -2310,7 +2308,7 @@ def test_server_create_with_wait_ok(self, mock_wait_for_status):
23102308
self.new_server.name, self.image, self.flavor, **kwargs
23112309
)
23122310
self.assertEqual(self.columns, columns)
2313-
self.assertEqual(self.datalist(), data)
2311+
self.assertTupleEqual(self.datalist(), data)
23142312

23152313
@mock.patch.object(common_utils, 'wait_for_status', return_value=False)
23162314
def test_server_create_with_wait_fails(self, mock_wait_for_status):
@@ -8209,7 +8207,7 @@ def setUp(self):
82098207
'image': {'id': self.image.id},
82108208
'flavor': {'id': self.flavor.id},
82118209
'tenant_id': 'tenant-id-xxx',
8212-
'networks': {'public': ['10.20.30.40', '2001:db8::f']},
8210+
'addresses': {'public': ['10.20.30.40', '2001:db8::f']},
82138211
}
82148212
self.compute_sdk_client.get_server_diagnostics.return_value = {
82158213
'test': 'test'
@@ -8236,7 +8234,6 @@ def setUp(self):
82368234
'id',
82378235
'image',
82388236
'name',
8239-
'networks',
82408237
'project_id',
82418238
'properties',
82428239
)
@@ -8245,12 +8242,11 @@ def setUp(self):
82458242
server.PowerStateColumn(
82468243
getattr(self.server, 'OS-EXT-STS:power_state')
82478244
),
8248-
format_columns.DictListColumn(self.server.networks),
82498245
self.flavor.name + " (" + self.flavor.id + ")",
82508246
self.server.id,
82518247
self.image.name + " (" + self.image.id + ")",
82528248
self.server.name,
8253-
{'public': ['10.20.30.40', '2001:db8::f']},
8249+
server.AddressesColumn({'public': ['10.20.30.40', '2001:db8::f']}),
82548250
'tenant-id-xxx',
82558251
format_columns.DictColumn({}),
82568252
)
@@ -9059,7 +9055,7 @@ def test_prep_server_detail(self, find_resource):
90599055
'image': {u'id': _image.id},
90609056
'flavor': {u'id': _flavor.id},
90619057
'tenant_id': u'tenant-id-xxx',
9062-
'networks': {u'public': [u'10.20.30.40', u'2001:db8::f']},
9058+
'addresses': {u'public': [u'10.20.30.40', u'2001:db8::f']},
90639059
'links': u'http://xxx.yyy.com',
90649060
'properties': '',
90659061
'volumes_attached': [{"id": "6344fe9d-ef20-45b2-91a6"}],
@@ -9079,7 +9075,7 @@ def test_prep_server_detail(self, find_resource):
90799075
),
90809076
'properties': '',
90819077
'volumes_attached': [{"id": "6344fe9d-ef20-45b2-91a6"}],
9082-
'addresses': format_columns.DictListColumn(_server.networks),
9078+
'addresses': format_columns.DictListColumn(_server.addresses),
90839079
'project_id': 'tenant-id-xxx',
90849080
}
90859081

@@ -9089,8 +9085,6 @@ def test_prep_server_detail(self, find_resource):
90899085
self.image_client,
90909086
_server,
90919087
)
9092-
# 'networks' is used to create _server. Remove it.
9093-
server_detail.pop('networks')
90949088

90959089
# Check the results.
90969090
self.assertCountEqual(info, server_detail)

0 commit comments

Comments
 (0)