Skip to content

Commit 03776d8

Browse files
committed
compute: Fix 'server * -f yaml' output
Make use of 'FormattableColumn'-derived formatters, which provide better output than what we were using before, particularly for the YAML output format. For example, compare before for the 'server show' command: $ openstack --os-compute-api-version 2.79 server show test-server -f yaml ... addresses: private=fdff:77e3:9bb4:0:f816:3eff:fe6d:a944, 10.0.0.44 flavor: disk='1', ephemeral='0', extra_specs.hw_rng:allowed='True', original_name='m1.tiny', ram='512', swap='0', vcpus='1' ... To after: $ openstack --os-compute-api-version 2.79 server show test-server -f yaml ... addresses: private: - fdff:77e3:9bb4:0:f816:3eff:fe6d:a944 - 10.0.0.44 flavor: disk: 1 ephemeral: 0 extra_specs: hw_rng:allowed: 'True' original_name: m1.tiny ram: 512 swap: 0 vcpus: 1 ... Similarly, compare before for 'server list': $ openstack --os-compute-api-version 2.79 server list -f yaml - ... Networks: private=fdff:77e3:9bb4:0:f816:3eff:fe6d:a944, 10.0.0.44 Power State: Running Properties: '' ... To after: $ openstack --os-compute-api-version 2.79 server list -f yaml - ... Networks: private: - fdff:77e3:9bb4:0:f816:3eff:fe6d:a944 - 10.0.0.44 Power State: 1 Properties: {} ... We also fix the human-readable output for the 'tags' field. Before: $ openstack --os-compute-api-version 2.79 server list ... | tags | ['bar', 'foo'] | After: $ openstack --os-compute-api-version 2.79 server list ... | tags | bar, foo | Change-Id: I7a8349106e211c57c4577b75326b39b88bd9ac1e Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
1 parent f2c4914 commit 03776d8

4 files changed

Lines changed: 99 additions & 115 deletions

File tree

openstackclient/compute/v2/server.py

Lines changed: 18 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import logging
2222
import os
2323

24+
from cliff import columns as cliff_columns
2425
import iso8601
2526
from novaclient import api_versions
2627
from novaclient.v2 import servers
@@ -41,28 +42,9 @@
4142
IMAGE_STRING_FOR_BFV = 'N/A (booted from volume)'
4243

4344

44-
def _format_servers_list_networks(networks):
45-
"""Return a formatted string of a server's networks
45+
class PowerStateColumn(cliff_columns.FormattableColumn):
46+
"""Generate a formatted string of a server's power state."""
4647

47-
:param networks: a Server.networks field
48-
:rtype: a string of formatted network addresses
49-
"""
50-
output = []
51-
for (network, addresses) in networks.items():
52-
if not addresses:
53-
continue
54-
addresses_csv = ', '.join(addresses)
55-
group = "%s=%s" % (network, addresses_csv)
56-
output.append(group)
57-
return '; '.join(output)
58-
59-
60-
def _format_servers_list_power_state(state):
61-
"""Return a formatted string of a server's power state
62-
63-
:param state: the power state number of a server
64-
:rtype: a string mapped to the power state number
65-
"""
6648
power_states = [
6749
'NOSTATE', # 0x00
6850
'Running', # 0x01
@@ -74,10 +56,11 @@ def _format_servers_list_power_state(state):
7456
'Suspended' # 0x07
7557
]
7658

77-
try:
78-
return power_states[state]
79-
except Exception:
80-
return 'N/A'
59+
def human_readable(self):
60+
try:
61+
return self.power_states[self._value]
62+
except Exception:
63+
return 'N/A'
8164

8265

8366
def _get_ip_address(addresses, address_type, ip_address_family):
@@ -169,7 +152,7 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True):
169152
except Exception:
170153
info['flavor'] = flavor_id
171154
else:
172-
info['flavor'] = utils.format_dict(flavor_info)
155+
info['flavor'] = format_columns.DictColumn(flavor_info)
173156

174157
if 'os-extended-volumes:volumes_attached' in info:
175158
info.update(
@@ -185,27 +168,23 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True):
185168
info.pop('security_groups'))
186169
}
187170
)
171+
if 'tags' in info:
172+
info.update({'tags': format_columns.ListColumn(info.pop('tags'))})
173+
188174
# NOTE(dtroyer): novaclient splits these into separate entries...
189175
# Format addresses in a useful way
190-
info['addresses'] = _format_servers_list_networks(server.networks)
176+
info['addresses'] = format_columns.DictListColumn(server.networks)
191177

192178
# Map 'metadata' field to 'properties'
193-
if not info['metadata']:
194-
info.update(
195-
{'properties': utils.format_dict(info.pop('metadata'))}
196-
)
197-
else:
198-
info.update(
199-
{'properties': format_columns.DictColumn(info.pop('metadata'))}
200-
)
179+
info['properties'] = format_columns.DictColumn(info.pop('metadata'))
201180

202181
# Migrate tenant_id to project_id naming
203182
if 'tenant_id' in info:
204183
info['project_id'] = info.pop('tenant_id')
205184

206185
# Map power state num to meaningful string
207186
if 'OS-EXT-STS:power_state' in info:
208-
info['OS-EXT-STS:power_state'] = _format_servers_list_power_state(
187+
info['OS-EXT-STS:power_state'] = PowerStateColumn(
209188
info['OS-EXT-STS:power_state'])
210189

211190
# Remove values that are long and not too useful
@@ -1873,10 +1852,9 @@ def take_action(self, parsed_args):
18731852
s, columns,
18741853
mixed_case_fields=mixed_case_fields,
18751854
formatters={
1876-
'OS-EXT-STS:power_state':
1877-
_format_servers_list_power_state,
1878-
'Networks': _format_servers_list_networks,
1879-
'Metadata': utils.format_dict,
1855+
'OS-EXT-STS:power_state': PowerStateColumn,
1856+
'Networks': format_columns.DictListColumn,
1857+
'Metadata': format_columns.DictColumn,
18801858
},
18811859
) for s in data
18821860
),

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

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
# License for the specific language governing permissions and limitations
1111
# under the License.
1212

13+
import itertools
1314
import json
1415
import time
1516
import uuid
@@ -346,6 +347,14 @@ def test_server_attach_detach_floating_ip(self):
346347
# DevStack without cells.
347348
self.skipTest("No Network service present")
348349

350+
def _chain_addresses(addresses):
351+
# Flatten a dict of lists mapping network names to IP addresses,
352+
# returning only the IP addresses
353+
#
354+
# >>> _chain_addresses({'private': ['10.1.0.32', '172.24.5.41']})
355+
# ['10.1.0.32', '172.24.5.41']
356+
return itertools.chain(*[*addresses.values()])
357+
349358
cmd_output = self.server_create()
350359
name = cmd_output['name']
351360
self.wait_for_status(name, "ACTIVE")
@@ -387,7 +396,7 @@ def test_server_attach_detach_floating_ip(self):
387396
'server show -f json ' +
388397
name
389398
))
390-
if floating_ip not in cmd_output['addresses']:
399+
if floating_ip not in _chain_addresses(cmd_output['addresses']):
391400
# Hang out for a bit and try again
392401
print('retrying floating IP check')
393402
wait_time += 10
@@ -397,7 +406,7 @@ def test_server_attach_detach_floating_ip(self):
397406

398407
self.assertIn(
399408
floating_ip,
400-
cmd_output['addresses'],
409+
_chain_addresses(cmd_output['addresses']),
401410
)
402411

403412
# detach ip
@@ -417,7 +426,7 @@ def test_server_attach_detach_floating_ip(self):
417426
'server show -f json ' +
418427
name
419428
))
420-
if floating_ip in cmd_output['addresses']:
429+
if floating_ip in _chain_addresses(cmd_output['addresses']):
421430
# Hang out for a bit and try again
422431
print('retrying floating IP check')
423432
wait_time += 10
@@ -431,7 +440,7 @@ def test_server_attach_detach_floating_ip(self):
431440
))
432441
self.assertNotIn(
433442
floating_ip,
434-
cmd_output['addresses'],
443+
_chain_addresses(cmd_output['addresses']),
435444
)
436445

437446
def test_server_reboot(self):
@@ -856,8 +865,7 @@ def test_server_create_with_none_network(self):
856865
server = json.loads(self.openstack(
857866
'server show -f json ' + server_name
858867
))
859-
self.assertIsNotNone(server['addresses'])
860-
self.assertEqual('', server['addresses'])
868+
self.assertEqual({}, server['addresses'])
861869

862870
def test_server_create_with_security_group(self):
863871
"""Test server create with security group ID and name"""

0 commit comments

Comments
 (0)