Use raw values when non-table formatter is used

neutron CLI supports various output formats such as json, yaml
(e.g. neutron net-list -f json),
but the current output formattings is optimized to the table
formatter to get human-friendly output in normal CLI use
(For example, new lines are inserted).
However, this kind of formatting is unnecessary when other output
formatters are used and prevents users from getting proper data.

This commit changes to skip format_output_data() method when
output formatters other than 'table' are used.
Now we have proper outputs in JSON, YAML or other formats.

In CreateCommand and ShowCommand, cleanup_output_data() is moved
to each command class because it should be called regardless of
a formatter type.

In ListCommand, value formatter for CSV is still supported for backward
compatiblity, but the value formatter for fixed_ips of the port resource
is removed because it brings no much value in CSV output.

Closes-Bug: #1524624
Change-Id: I2668fa90402d7119db69f09a20fb7c7270c9616e
This commit is contained in:
Akihiro Motoki 2015-12-10 04:57:11 +09:00
parent 8460b0dbb3
commit 7bf8f229bb
3 changed files with 99 additions and 13 deletions

View File

@ -429,7 +429,6 @@ class NeutronCommand(command.Command):
pass
def format_output_data(self, data):
self.cleanup_output_data(data)
# Modify data to make it more readable
if self.resource in data:
for k, v in six.iteritems(data[self.resource]):
@ -484,7 +483,9 @@ class CreateCommand(NeutronCommand, show.ShowOne):
data = obj_creator(self.parent_id, body)
else:
data = obj_creator(body)
self.format_output_data(data)
self.cleanup_output_data(data)
if parsed_args.formatter == 'table':
self.format_output_data(data)
info = self.resource in data and data[self.resource] or None
if info:
if parsed_args.formatter == 'table':
@ -754,9 +755,14 @@ class ListCommand(NeutronCommand, lister.Lister):
# Also Keep their order the same as in list_columns
_columns = [x for x in self.list_columns if x in _columns]
formatters = self._formatters
if hasattr(self, '_formatters_csv') and parsed_args.formatter == 'csv':
if parsed_args.formatter == 'table':
formatters = self._formatters
elif (parsed_args.formatter == 'csv'
and hasattr(self, '_formatters_csv')):
formatters = self._formatters_csv
else:
# For other formatters, we use raw value returned from neutron
formatters = {}
return (_columns, (utils.get_item_properties(
s, _columns, formatters=formatters, )
@ -814,7 +820,9 @@ class ShowCommand(NeutronCommand, show.ShowOne):
data = obj_shower(_id, self.parent_id, **params)
else:
data = obj_shower(_id, **params)
self.format_output_data(data)
self.cleanup_output_data(data)
if parsed_args.formatter == 'table':
self.format_output_data(data)
resource = data[self.resource]
if self.resource in data:
return zip(*sorted(six.iteritems(resource)))

View File

@ -33,13 +33,6 @@ def _format_fixed_ips(port):
return ''
def _format_fixed_ips_csv(port):
try:
return jsonutils.dumps(port['fixed_ips'])
except (TypeError, KeyError):
return ''
def _add_updatable_args(parser):
parser.add_argument(
'--name',
@ -90,7 +83,6 @@ class ListPort(neutronV20.ListCommand):
resource = 'port'
_formatters = {'fixed_ips': _format_fixed_ips, }
_formatters_csv = {'fixed_ips': _format_fixed_ips_csv, }
list_columns = ['id', 'name', 'mac_address', 'fixed_ips']
pagination_support = True
sorting_support = True

View File

@ -16,6 +16,7 @@
import contextlib
import itertools
import json
import sys
import fixtures
@ -25,11 +26,13 @@ from oslotest import base
import requests
import six
import six.moves.urllib.parse as urlparse
import yaml
from neutronclient.common import constants
from neutronclient.common import exceptions
from neutronclient.common import utils
from neutronclient.neutron import v2_0 as neutronV2_0
from neutronclient.neutron.v2_0 import network
from neutronclient import shell
from neutronclient.v2_0 import client
@ -1020,3 +1023,86 @@ class GeneratorWithMetaTest(base.BaseTestCase):
self.assertTrue(hasattr(obj, 'request_ids'))
self.assertEqual([REQUEST_ID], obj.request_ids)
class CLITestV20OutputFormatter(CLITestV20Base):
def _test_create_resource_with_formatter(self, fmt):
resource = 'network'
cmd = network.CreateNetwork(MyApp(sys.stdout), None)
args = ['-f', fmt, 'myname']
position_names = ['name']
position_values = ['myname']
self._test_create_resource(resource, cmd, 'myname', 'myid', args,
position_names, position_values)
def test_create_resource_table(self):
self._test_create_resource_with_formatter('table')
print(self.fake_stdout.content)
# table data is contains in the third element.
data = self.fake_stdout.content[2].split('\n')
self.assertTrue(any(' id ' in d for d in data))
self.assertTrue(any(' name ' in d for d in data))
def test_create_resource_json(self):
self._test_create_resource_with_formatter('json')
data = json.loads(self.fake_stdout.make_string())
self.assertEqual('myname', data['name'])
self.assertEqual('myid', data['id'])
def test_create_resource_yaml(self):
self._test_create_resource_with_formatter('yaml')
data = yaml.load(self.fake_stdout.make_string())
self.assertEqual('myname', data['name'])
self.assertEqual('myid', data['id'])
def _test_show_resource_with_formatter(self, fmt):
resource = 'network'
cmd = network.ShowNetwork(MyApp(sys.stdout), None)
args = ['-f', fmt, '-F', 'id', '-F', 'name', 'myid']
self._test_show_resource(resource, cmd, 'myid',
args, ['id', 'name'])
def test_show_resource_table(self):
self._test_show_resource_with_formatter('table')
data = self.fake_stdout.content[0].split('\n')
self.assertTrue(any(' id ' in d for d in data))
self.assertTrue(any(' name ' in d for d in data))
def test_show_resource_json(self):
self._test_show_resource_with_formatter('json')
data = json.loads(''.join(self.fake_stdout.content))
self.assertEqual('myname', data['name'])
self.assertEqual('myid', data['id'])
def test_show_resource_yaml(self):
self._test_show_resource_with_formatter('yaml')
data = yaml.load(''.join(self.fake_stdout.content))
self.assertEqual('myname', data['name'])
self.assertEqual('myid', data['id'])
def _test_list_resources_with_formatter(self, fmt):
resources = 'networks'
cmd = network.ListNetwork(MyApp(sys.stdout), None)
# ListNetwork has its own extend_list, so we need to stub out it
# to avoid an extra API call.
self.mox.StubOutWithMock(network.ListNetwork, "extend_list")
network.ListNetwork.extend_list(mox.IsA(list), mox.IgnoreArg())
self._test_list_resources(resources, cmd, output_format=fmt)
def test_list_resources_table(self):
self._test_list_resources_with_formatter('table')
data = self.fake_stdout.content[0].split('\n')
self.assertTrue(any(' id ' in d for d in data))
self.assertTrue(any(' myid1 ' in d for d in data))
self.assertTrue(any(' myid2 ' in d for d in data))
def test_list_resources_json(self):
self._test_list_resources_with_formatter('json')
data = json.loads(''.join(self.fake_stdout.content))
self.assertEqual(['myid1', 'myid2'], [d['id'] for d in data])
def test_list_resources_yaml(self):
self._test_list_resources_with_formatter('yaml')
data = yaml.load(''.join(self.fake_stdout.content))
self.assertEqual(['myid1', 'myid2'], [d['id'] for d in data])