Merge "Add '--fields' to show more columns for baymodel-list"

This commit is contained in:
Jenkins
2016-05-13 14:36:03 +00:00
committed by Gerrit Code Review
4 changed files with 151 additions and 2 deletions

View File

@@ -287,3 +287,40 @@ def exit(msg=''):
if msg:
print (msg, file=sys.stderr)
sys.exit(1)
def _format_field_name(attr):
"""Format an object attribute in a human-friendly way."""
# Split at ':' and leave the extension name as-is.
parts = attr.rsplit(':', 1)
name = parts[-1].replace('_', ' ')
# Don't title() on mixed case
if name.isupper() or name.islower():
name = name.title()
parts[-1] = name
return ': '.join(parts)
def make_field_formatter(attr, filters=None):
"""Given an object attribute.
Return a formatted field name and a formatter suitable for passing to
print_list.
Optionally pass a dict mapping attribute names to a function. The function
will be passed the value of the attribute and should return the string to
display.
"""
filter_ = None
if filters:
filter_ = filters.get(attr)
def get_field(obj):
field = getattr(obj, attr, '')
if field and filter_:
field = filter_(field)
return field
name = _format_field_name(attr)
formatter = get_field
return name, formatter

View File

@@ -70,8 +70,10 @@ class TestCommandLineArgument(utils.TestCase):
self.addCleanup(loader.stop)
self.addCleanup(session.stop)
def _test_arg_success(self, command):
def _test_arg_success(self, command, keyword=None):
stdout, stderr = self.shell(command)
if keyword:
self.assertTrue(keyword in (stdout + stderr))
def _test_arg_failure(self, command, error_msg):
stdout, stderr = self.shell(command, (2,))

View File

@@ -14,7 +14,24 @@
import mock
from magnumclient.common.apiclient import exceptions
from magnumclient.tests.v1 import shell_test_base
from magnumclient.v1.baymodels import BayModel
class FakeBayModel(BayModel):
def __init__(self, manager=None, info={}, **kwargs):
BayModel.__init__(self, manager=manager, info=info)
self.apiserver_port = kwargs.get('apiserver_port', None)
self.uuid = kwargs.get('uuid', 'x')
self.links = kwargs.get('links', [])
self.server_type = kwargs.get('server_type', 'vm')
self.image_id = kwargs.get('image_id', 'x')
self.tls_disabled = kwargs.get('tls_disabled', False)
self.registry_enabled = kwargs.get('registry_enabled', False)
self.coe = kwargs.get('coe', 'x')
self.public = kwargs.get('public', False)
self.name = kwargs.get('name', 'x')
class ShellTest(shell_test_base.TestCommandLineArgument):
@@ -256,6 +273,29 @@ class ShellTest(shell_test_base.TestCommandLineArgument):
'--sort-key uuid')
self.assertTrue(mock_list.called)
@mock.patch('magnumclient.v1.baymodels.BayModelManager.list')
def test_baymodel_list_ignored_duplicated_field(self, mock_list):
mock_list.return_value = [FakeBayModel()]
self._test_arg_success('baymodel-list --fields coe,coe,coe,name,name',
keyword='\n| uuid | name | Coe |\n')
# Output should be
# +------+------+-----+
# | uuid | name | Coe |
# +------+------+-----+
# | x | x | x |
# +------+------+-----+
self.assertTrue(mock_list.called)
@mock.patch('magnumclient.v1.baymodels.BayModelManager.list')
def test_baymodel_list_failure_with_invalid_field(self, mock_list):
mock_list.return_value = [FakeBayModel()]
_error_msg = [".*?^Non-existent fields are specified: ['xxx','zzz']"]
self.assertRaises(exceptions.CommandError,
self._test_arg_failure,
'baymodel-list --fields xxx,coe,zzz',
_error_msg)
self.assertTrue(mock_list.called)
@mock.patch('magnumclient.v1.baymodels.BayModelManager.list')
def test_baymodel_list_failure_invalid_arg(self, mock_list):
_error_msg = [

View File

@@ -14,8 +14,10 @@
import os.path
from magnumclient.common.apiclient import exceptions
from magnumclient.common import cliutils as utils
from magnumclient.common import utils as magnum_utils
from magnumclient.i18n import _
def _show_baymodel(baymodel):
@@ -159,12 +161,23 @@ def do_baymodel_show(cs, args):
metavar='<sort-dir>',
choices=['desc', 'asc'],
help='Direction to sort. "asc" or "desc".')
@utils.arg('--fields',
default=None,
metavar='<fields>',
help=_('Comma-separated list of fields to display. '
'Available fields: uuid, name, coe, image_id, public, link, '
'apiserver_port, server_type, tls_disabled, registry_enabled'
)
)
def do_baymodel_list(cs, args):
"""Print a list of baymodels."""
nodes = cs.baymodels.list(limit=args.limit,
sort_key=args.sort_key,
sort_dir=args.sort_dir)
columns = ('uuid', 'name')
columns = ['uuid', 'name']
columns += _get_list_table_columns_and_formatters(
args.fields, nodes,
exclude_fields=(c.lower() for c in columns))[0]
utils.print_list(nodes, columns,
{'versions': magnum_utils.print_list_field('versions')},
sortby_index=None)
@@ -194,3 +207,60 @@ def do_baymodel_update(cs, args):
baymodel = cs.baymodels.update(args.baymodel, patch)
_show_baymodel(baymodel)
def _get_list_table_columns_and_formatters(fields, objs, exclude_fields=(),
filters=None):
"""Check and add fields to output columns.
If there is any value in fields that not an attribute of obj,
CommandError will be raised.
If fields has duplicate values (case sensitive), we will make them unique
and ignore duplicate ones.
:param fields: A list of string contains the fields to be printed.
:param objs: An list of object which will be used to check if field is
valid or not. Note, we don't check fields if obj is None or
empty.
:param exclude_fields: A tuple of string which contains the fields to be
excluded.
:param filters: A dictionary defines how to get value from fields, this
is useful when field's value is a complex object such as
dictionary.
:return: columns, formatters.
columns is a list of string which will be used as table header.
formatters is a dictionary specifies how to display the value
of the field.
They can be [], {}.
:raise: magnumclient.common.apiclient.exceptions.CommandError.
"""
if objs and isinstance(objs, list):
obj = objs[0]
else:
obj = None
fields = None
columns = []
formatters = {}
if fields:
non_existent_fields = []
exclude_fields = set(exclude_fields)
for field in fields.split(','):
if not hasattr(obj, field):
non_existent_fields.append(field)
continue
if field in exclude_fields:
continue
field_title, formatter = utils.make_field_formatter(field, filters)
columns.append(field_title)
formatters[field_title] = formatter
exclude_fields.add(field)
if non_existent_fields:
raise exceptions.CommandError(
_("Non-existent fields are specified: %s") %
non_existent_fields
)
return columns, formatters