Make column ordering consistent and predictable

This changes how we sort columns in listings and how we display the
column headings. The default columns are now stored as lists so that
they are the same from run to run. This simplifies some of the logic in
the shell modules as well. Instead of keeping static mappings of
attributes to column headings, we now use some simple python logic, to
title case the columns.

This commit covers:

- cell-* commands
- cloud-* commands
- host-* commands
- project-* commands
- region-* commands

Finally, we noticed that the cloud-list and region-list commands were
behaving differently from the rest of the -list commands. This
unifies the interface to add the --detail flag.

Closes-bug: #1659103
Closes-bug: #1659427
Closes-bug: #1668221
Change-Id: If5906780e501c7b9ba93ecf54a7bcf6db5ddfa1c
This commit is contained in:
Ian Cordasco 2017-03-02 15:01:09 -06:00
parent cd1c025ae0
commit 683f342506
19 changed files with 244 additions and 209 deletions

View File

@ -51,6 +51,27 @@ def add_arg(func, *args, **kwargs):
func.arguments.insert(0, (args, kwargs))
def field_labels_from(attributes):
"""Generate a list of slightly more human readable field names.
This takes the list of fields/attributes on the object and makes them
easier to read.
:param list attributes:
The attribute names to convert. For example, ``["parent_id"]``.
:returns:
List of field names. For example ``["Parent Id"]``
:rtype:
list
Example:
>>> field_labels_from(["id", "name", "cloud_id"])
["Id", "Name", "Cloud Id"]
"""
return [field.replace('_', ' ').title() for field in attributes]
def print_list(objs, fields, formatters=None, sortby_index=0,
mixed_case_fields=None, field_labels=None):
"""Print a list or objects as a table, one row per object.

View File

@ -18,6 +18,7 @@ from oslo_utils import encodeutils
import prettytable
import six
from cratonclient.common import cliutils
from cratonclient.formatters import base
@ -28,7 +29,7 @@ class Formatter(base.Formatter):
"""Set-up after initialization."""
self.fields = []
self.formatters = {}
self.sortby_index = 0
self.sortby_index = None
self.mixed_case_fields = set([])
self.field_labels = []
self.dict_property = "Property"
@ -71,7 +72,7 @@ class Formatter(base.Formatter):
if fields is not None:
self.fields = fields
if field_labels is None:
self.field_labels = self.fields
self.field_labels = cliutils.field_labels_from(self.fields)
elif len(field_labels) != len(self.fields):
raise ValueError(
"Field labels list %(labels)s has different number "

View File

@ -16,7 +16,21 @@ from __future__ import print_function
from cratonclient.common import cliutils
from cratonclient import exceptions as exc
from cratonclient.v1 import cells
DEFAULT_CELL_FIELDS = [
'id',
'name',
'cloud_id',
'region_id',
'created_at',
]
CELL_FIELDS = DEFAULT_CELL_FIELDS + [
'updated_at',
'note',
'variables',
'project_id',
]
@cliutils.arg('id',
@ -52,7 +66,7 @@ def do_cell_show(cc, args):
@cliutils.arg('--fields',
nargs='+',
metavar='<fields>',
default=[],
default=DEFAULT_CELL_FIELDS,
help='Space-separated list of fields to display. '
'Only these fields will be fetched from the server. '
'Can not be used when "--detail" is specified')
@ -73,7 +87,6 @@ def do_cell_show(cc, args):
def do_cell_list(cc, args):
"""Print list of cells which are registered with the Craton service."""
params = {}
default_fields = ['id', 'name']
if args.cloud is not None:
params['cloud_id'] = args.cloud
if args.limit is not None:
@ -85,27 +98,29 @@ def do_cell_list(cc, args):
if args.all is True:
params['limit'] = 100
if args.fields and args.detail:
raise exc.CommandError('Cannot specify both --fields and --detail.')
if args.detail:
fields = cells.CELL_FIELDS
if args.fields and args.fields == DEFAULT_CELL_FIELDS:
args.fields = CELL_FIELDS
else:
raise exc.CommandError(
'Cannot specify both --fields and --detail.'
)
params['detail'] = args.detail
elif args.fields:
try:
fields = {x: cells.CELL_FIELDS[x] for x in args.fields}
except KeyError as keyerr:
raise exc.CommandError('Invalid field "{}"'.format(keyerr.args[0]))
else:
fields = {x: cells.CELL_FIELDS[x] for x in default_fields}
fields = args.fields
for field in fields:
if field not in CELL_FIELDS:
raise exc.CommandError(
'Invalid field "{}"'.format(field)
)
sort_key = args.sort_key and args.sort_key.lower()
if sort_key is not None:
if sort_key not in cells.CELL_FIELDS:
if sort_key not in CELL_FIELDS:
raise exc.CommandError(
('"--sort-key" value was "{}" but should '
'be one of: {}').format(
args.sort_key,
', '.join(cells.CELL_FIELDS.keys())
', '.join(CELL_FIELDS)
)
)
params['sort_key'] = sort_key
@ -117,7 +132,7 @@ def do_cell_list(cc, args):
params['autopaginate'] = args.all
listed_cells = cc.cells.list(**params)
args.formatter.configure(fields=list(fields)).handle(listed_cells)
args.formatter.configure(fields=fields).handle(listed_cells)
@cliutils.arg('-n', '--name',
@ -141,7 +156,7 @@ def do_cell_list(cc, args):
def do_cell_create(cc, args):
"""Register a new cell with the Craton service."""
fields = {k: v for (k, v) in vars(args).items()
if k in cells.CELL_FIELDS and not (v is None)}
if k in CELL_FIELDS and not (v is None)}
cell = cc.cells.create(**fields)
args.formatter.configure(wrap=72).handle(cell)
@ -168,7 +183,7 @@ def do_cell_create(cc, args):
def do_cell_update(cc, args):
"""Update a cell that is registered with the Craton service."""
fields = {k: v for (k, v) in vars(args).items()
if k in cells.CELL_FIELDS and not (v is None)}
if k in CELL_FIELDS and not (v is None)}
cell_id = fields.pop('id')
if not fields:
raise exc.CommandError(

View File

@ -14,7 +14,18 @@ from __future__ import print_function
from cratonclient.common import cliutils
from cratonclient import exceptions as exc
from cratonclient.v1 import clouds
DEFAULT_CLOUD_FIELDS = [
'id',
'name',
'created_at',
]
CLOUD_FIELDS = DEFAULT_CLOUD_FIELDS + [
'updated_at',
'note',
'project_id',
]
@cliutils.arg('-n', '--name',
@ -26,7 +37,7 @@ from cratonclient.v1 import clouds
def do_cloud_create(cc, args):
"""Register a new cloud with the Craton service."""
fields = {k: v for (k, v) in vars(args).items()
if k in clouds.CLOUD_FIELDS and not (v is None)}
if k in CLOUD_FIELDS and not (v is None)}
cloud = cc.clouds.create(**fields)
args.formatter.configure(wrap=72).handle(cloud)
@ -35,7 +46,7 @@ def do_cloud_create(cc, args):
@cliutils.arg('--fields',
nargs='+',
metavar='<fields>',
default=[],
default=DEFAULT_CLOUD_FIELDS,
help='Comma-separated list of fields to display. '
'Only these fields will be fetched from the server. '
'Can not be used when "--detail" is specified')
@ -45,6 +56,10 @@ def do_cloud_create(cc, args):
help='Retrieve and show all clouds. This will override '
'the provided value for --limit and automatically '
'retrieve each page of results.')
@cliutils.arg('--detail',
action='store_true',
default=False,
help='Show detailed information about all clouds.')
@cliutils.arg('--limit',
metavar='<limit>',
type=int,
@ -56,7 +71,6 @@ def do_cloud_create(cc, args):
def do_cloud_list(cc, args):
"""List all clouds."""
params = {}
default_fields = ['id', 'name']
if args.limit is not None:
if args.limit < 0:
raise exc.CommandError('Invalid limit specified. Expected '
@ -66,13 +80,22 @@ def do_cloud_list(cc, args):
if args.all is True:
params['limit'] = 100
if args.fields:
try:
fields = {x: clouds.CLOUD_FIELDS[x] for x in args.fields}
except KeyError as err:
raise exc.CommandError('Invalid field "{}"'.format(err.args[0]))
else:
fields = default_fields
if args.detail:
if args.fields and args.fields == DEFAULT_CLOUD_FIELDS:
args.fields = CLOUD_FIELDS
else:
raise exc.CommandError(
'Cannot specify both --fields and --detail.'
)
params['detail'] = args.detail
fields = args.fields
for field in args.fields:
if field not in CLOUD_FIELDS:
raise exc.CommandError(
'Invalid field "{}"'.format(field)
)
params['marker'] = args.marker
params['autopaginate'] = args.all
@ -102,7 +125,7 @@ def do_cloud_show(cc, args):
def do_cloud_update(cc, args):
"""Update a cloud that is registered with the Craton service."""
fields = {k: v for (k, v) in vars(args).items()
if k in clouds.CLOUD_FIELDS and not (v is None)}
if k in CLOUD_FIELDS and not (v is None)}
item_id = fields.pop('id')
if not fields:
raise exc.CommandError(

View File

@ -16,7 +16,28 @@ from __future__ import print_function
from cratonclient.common import cliutils
from cratonclient import exceptions as exc
from cratonclient.v1 import hosts
DEFAULT_HOST_FIELDS = [
'id',
'name',
'active',
'device_type',
'ip_address',
'cloud_id',
'region_id',
'cell_id',
'created_at',
]
HOST_FIELDS = DEFAULT_HOST_FIELDS + [
'updated_at',
'note',
'variables',
'labels',
'parent_id',
'project_id',
]
@cliutils.arg('id',
@ -57,7 +78,7 @@ def do_host_show(cc, args):
@cliutils.arg('--fields',
nargs='+',
metavar='<fields>',
default=[],
default=DEFAULT_HOST_FIELDS,
help='Space-separated list of fields to display. '
'Only these fields will be fetched from the server. '
'Can not be used when "--detail" is specified')
@ -94,7 +115,6 @@ def do_host_show(cc, args):
def do_host_list(cc, args):
"""Print list of hosts which are registered with the Craton service."""
params = {}
default_fields = ['id', 'name', 'device_type', 'active', 'cell_id']
if args.cell is not None:
params['cell_id'] = args.cell
if args.cloud is not None:
@ -116,26 +136,29 @@ def do_host_list(cc, args):
if args.all is True:
params['limit'] = 100
if args.fields and args.detail:
raise exc.CommandError('Cannot specify both --fields and --detail.')
if args.detail:
fields = hosts.HOST_FIELDS
if args.fields and args.fields == DEFAULT_HOST_FIELDS:
args.fields = HOST_FIELDS
else:
raise exc.CommandError(
'Cannot specify both --fields and --detail.'
)
params['detail'] = args.detail
elif args.fields:
try:
fields = {x: hosts.HOST_FIELDS[x] for x in args.fields}
except KeyError as keyerr:
raise exc.CommandError('Invalid field "{}"'.format(keyerr.args[0]))
else:
fields = {x: hosts.HOST_FIELDS[x] for x in default_fields}
fields = args.fields
for field in args.fields:
if field not in HOST_FIELDS:
raise exc.CommandError(
'Invalid field "{}"'.format(field)
)
sort_key = args.sort_key and args.sort_key.lower()
if sort_key is not None:
if sort_key not in hosts.HOST_FIELDS:
if sort_key not in HOST_FIELDS:
raise exc.CommandError(
'{0} is an invalid key for sorting, valid values for '
'--sort-key are: {1}'.format(
args.sort_key, hosts.HOST_FIELDS.keys()
args.sort_key, HOST_FIELDS
)
)
params['sort_key'] = sort_key
@ -147,7 +170,7 @@ def do_host_list(cc, args):
params['autopaginate'] = args.all
host_list = cc.hosts.list(**params)
args.formatter.configure(fields=list(fields)).handle(host_list)
args.formatter.configure(fields=fields).handle(host_list)
@cliutils.arg('-n', '--name',
@ -191,7 +214,7 @@ def do_host_list(cc, args):
def do_host_create(cc, args):
"""Register a new host with the Craton service."""
fields = {k: v for (k, v) in vars(args).items()
if k in hosts.HOST_FIELDS and (v or v is False)}
if k in HOST_FIELDS and (v or v is False)}
host = cc.hosts.create(**fields)
args.formatter.configure(wrap=72).handle(host)
@ -232,7 +255,7 @@ def do_host_create(cc, args):
def do_host_update(cc, args):
"""Update a host that is registered with the Craton service."""
fields = {k: v for (k, v) in vars(args).items()
if k in hosts.HOST_FIELDS and (v or v is False)}
if k in HOST_FIELDS and (v or v is False)}
item_id = fields.pop('id')
host = cc.hosts.update(item_id, **fields)
print("Host {0} has been successfully updated.".format(host.id))

View File

@ -16,7 +16,17 @@ from __future__ import print_function
from cratonclient.common import cliutils
from cratonclient import exceptions as exc
from cratonclient.v1 import projects
DEFAULT_PROJECT_FIELDS = [
'id',
'name',
]
PROJECT_FIELDS = DEFAULT_PROJECT_FIELDS + [
'created_at',
'updated_at',
]
@cliutils.arg('id',
@ -38,7 +48,7 @@ def do_project_show(cc, args):
@cliutils.arg('--fields',
nargs='+',
metavar='<fields>',
default=[],
default=DEFAULT_PROJECT_FIELDS,
help='Space-separated list of fields to display. '
'Only these fields will be fetched from the server. '
'Can not be used when "--detail" is specified')
@ -59,7 +69,6 @@ def do_project_show(cc, args):
def do_project_list(cc, args):
"""Print list of projects which are registered with the Craton service."""
params = {}
default_fields = ['id', 'name']
if args.limit is not None:
if args.limit < 0:
raise exc.CommandError('Invalid limit specified. Expected '
@ -69,20 +78,24 @@ def do_project_list(cc, args):
if args.all is True:
params['limit'] = 100
if args.fields and args.detail:
raise exc.CommandError('Cannot specify both --fields and --detail.')
if args.detail:
if args.fields and args.fields == DEFAULT_PROJECT_FIELDS:
args.fields = PROJECT_FIELDS
else:
raise exc.CommandError(
'Cannot specify both --fields and --detail.'
)
fields = args.fields
for field in fields:
if field not in PROJECT_FIELDS:
raise exc.CommandError(
'Invalid field "{}"'.format(field)
)
if args.name:
params['name'] = args.name
if args.detail:
fields = projects.PROJECT_FIELDS
elif args.fields:
try:
fields = {x: projects.PROJECT_FIELDS[x] for x in args.fields}
except KeyError as keyerr:
raise exc.CommandError('Invalid field "{}"'.format(keyerr.args[0]))
else:
fields = {x: projects.PROJECT_FIELDS[x] for x in default_fields}
params['marker'] = args.marker
params['autopaginate'] = args.all
@ -97,7 +110,7 @@ def do_project_list(cc, args):
def do_project_create(cc, args):
"""Register a new project with the Craton service."""
fields = {k: v for (k, v) in vars(args).items()
if k in projects.PROJECT_FIELDS and not (v is None)}
if k in PROJECT_FIELDS and not (v is None)}
project = cc.projects.create(**fields)
args.formatter.configure(wrap=72).handle(project)

View File

@ -14,7 +14,19 @@ from __future__ import print_function
from cratonclient.common import cliutils
from cratonclient import exceptions as exc
from cratonclient.v1 import regions
DEFAULT_REGION_FIELDS = [
'id',
'name',
'cloud_id',
]
REGION_FIELDS = DEFAULT_REGION_FIELDS + [
'project_id',
'note',
'created_at',
'updated_at',
]
@cliutils.arg('-n', '--name',
@ -32,7 +44,7 @@ from cratonclient.v1 import regions
def do_region_create(cc, args):
"""Register a new region with the Craton service."""
fields = {k: v for (k, v) in vars(args).items()
if k in regions.REGION_FIELDS and not (v is None)}
if k in REGION_FIELDS and not (v is None)}
region = cc.regions.create(**fields)
args.formatter.configure(wrap=72).handle(region)
@ -45,10 +57,14 @@ def do_region_create(cc, args):
@cliutils.arg('--fields',
nargs='+',
metavar='<fields>',
default=[],
default=DEFAULT_REGION_FIELDS,
help='Space-separated list of fields to display. '
'Only these fields will be fetched from the server. '
'Can not be used when "--detail" is specified')
@cliutils.arg('--detail',
action='store_true',
default=False,
help='Show detailed information about the regions.')
@cliutils.arg('--all',
action='store_true',
default=False,
@ -66,7 +82,6 @@ def do_region_create(cc, args):
def do_region_list(cc, args):
"""List all regions."""
params = {}
default_fields = ['id', 'name']
if args.cloud is not None:
params['cloud_id'] = args.cloud
if args.limit is not None:
@ -78,13 +93,21 @@ def do_region_list(cc, args):
if args.all is True:
params['limit'] = 100
if args.fields:
try:
fields = {x: regions.REGION_FIELDS[x] for x in args.fields}
except KeyError as err:
raise exc.CommandError('Invalid field "{}"'.format(err.args[0]))
else:
fields = default_fields
if args.detail:
if args.fields and args.fields == DEFAULT_REGION_FIELDS:
args.fields = REGION_FIELDS
else:
raise exc.CommandError(
'Cannot specify both --fields and --detail.'
)
params['detail'] = args.detail
fields = args.fields
for field in args.fields:
if field not in REGION_FIELDS:
raise exc.CommandError(
'Invalid field "{}"'.format(field)
)
params['marker'] = args.marker
params['autopaginate'] = args.all
@ -120,7 +143,7 @@ def do_region_show(cc, args):
def do_region_update(cc, args):
"""Update a region that is registered with the Craton service."""
fields = {k: v for (k, v) in vars(args).items()
if k in regions.REGION_FIELDS and not (v is None)}
if k in REGION_FIELDS and not (v is None)}
item_id = fields.pop('id')
if not fields:
raise exc.CommandError(

View File

@ -31,7 +31,7 @@ class TestTableFormatter(base.FormatterTestCase):
"""Verify we set up defaults for our PrettyTable formatter."""
self.assertEqual([], self.formatter.fields)
self.assertEqual({}, self.formatter.formatters)
self.assertEqual(0, self.formatter.sortby_index)
self.assertIsNone(self.formatter.sortby_index)
self.assertEqual(set([]), self.formatter.mixed_case_fields)
self.assertEqual([], self.formatter.field_labels)
self.assertEqual("Property", self.formatter.dict_property)
@ -63,14 +63,14 @@ class TestTableFormatter(base.FormatterTestCase):
# Assert defaults remain unchanged
self.assertEqual([], self.formatter.fields)
self.assertEqual([], self.formatter.field_labels)
self.assertEqual(0, self.formatter.sortby_index)
self.assertIsNone(self.formatter.sortby_index)
# Case 1: Just fields
def test_configure_fields_only(self):
"""Verify the logic for configuring fields."""
self.formatter.configure(fields=['id', 'name'])
self.assertListEqual(['id', 'name'], self.formatter.fields)
self.assertListEqual(['id', 'name'], self.formatter.field_labels)
self.assertListEqual(['Id', 'Name'], self.formatter.field_labels)
# Case 2: fields + field_labels
def test_configure_fields_and_field_labels(self):
@ -167,6 +167,7 @@ class TestTableFormatter(base.FormatterTestCase):
def test_sortby_kwargs(self):
"""Verify sortby_kwargs relies on sortby_index."""
self.formatter.field_labels = ['id', 'created_at']
self.formatter.sortby_index = 0
self.assertDictEqual({'sortby': 'id'}, self.formatter.sortby_kwargs())
self.formatter.sortby_index = 1
@ -219,10 +220,10 @@ class TestTableFormatter(base.FormatterTestCase):
self.formatter.handle_generator(crud.Resource(mock.Mock(), info)
for info in info_list)
PrettyTable.assert_called_once_with(['id', 'Name'])
PrettyTable.assert_called_once_with(['Id', 'Name'])
self.assertListEqual(
[mock.call([i, 'Test Resource']) for i in range(15)],
mocktable.add_row.call_args_list,
)
mocktable.get_string.assert_called_once_with(sortby='id')
mocktable.get_string.assert_called_once_with()
self.print_.assert_called_once_with('')

View File

@ -99,3 +99,8 @@ class TestShellCommandUsingPrintList(TestShellCommand):
kwargs = self.formatter.configure.call_args[1]
self.assertListEqual(expected_fields,
sorted(kwargs['fields']))
def assertFieldsEqualTo(self, expected_fields):
"""Assert the sorted fields parameter is equal expected_fields."""
kwargs = self.formatter.configure.call_args[1]
self.assertListEqual(expected_fields, kwargs['fields'])

View File

@ -17,7 +17,6 @@ import mock
from cratonclient import exceptions
from cratonclient.shell.v1 import cells_shell
from cratonclient.tests.unit.shell import base
from cratonclient.v1 import cells
class TestDoShellShow(base.TestShellCommandUsingPrintDict):
@ -54,7 +53,7 @@ class TestDoCellList(base.TestShellCommandUsingPrintList):
kwargs.setdefault('limit', None)
kwargs.setdefault('sort_key', None)
kwargs.setdefault('sort_dir', 'asc')
kwargs.setdefault('fields', [])
kwargs.setdefault('fields', cells_shell.DEFAULT_CELL_FIELDS)
kwargs.setdefault('marker', None)
kwargs.setdefault('all', False)
return super(TestDoCellList, self).args_for(**kwargs)
@ -71,7 +70,7 @@ class TestDoCellList(base.TestShellCommandUsingPrintList):
autopaginate=False,
marker=None,
)
self.assertSortedFieldsEqualTo(['id', 'name'])
self.assertFieldsEqualTo(cells_shell.DEFAULT_CELL_FIELDS)
def test_with_cloud_id(self):
"""Verify the behaviour of do_cell_list with mostly default values."""
@ -86,7 +85,7 @@ class TestDoCellList(base.TestShellCommandUsingPrintList):
autopaginate=False,
marker=None,
)
self.assertSortedFieldsEqualTo(['id', 'name'])
self.assertFieldsEqualTo(cells_shell.DEFAULT_CELL_FIELDS)
def test_negative_limit(self):
"""Ensure we raise an exception for negative limits."""
@ -108,7 +107,7 @@ class TestDoCellList(base.TestShellCommandUsingPrintList):
autopaginate=False,
marker=None,
)
self.assertSortedFieldsEqualTo(['id', 'name'])
self.assertFieldsEqualTo(cells_shell.DEFAULT_CELL_FIELDS)
def test_valid_sort_key(self):
"""Verify that we pass on our sort key."""
@ -123,7 +122,7 @@ class TestDoCellList(base.TestShellCommandUsingPrintList):
autopaginate=False,
marker=None,
)
self.assertSortedFieldsEqualTo(['id', 'name'])
self.assertFieldsEqualTo(cells_shell.DEFAULT_CELL_FIELDS)
def test_invalid_sort_key(self):
"""Verify that do not we pass on our sort key."""
@ -145,7 +144,7 @@ class TestDoCellList(base.TestShellCommandUsingPrintList):
autopaginate=False,
marker=None,
)
self.assertSortedFieldsEqualTo(sorted(list(cells.CELL_FIELDS)))
self.assertFieldsEqualTo(cells_shell.CELL_FIELDS)
def test_raises_exception_with_detail_and_fields(self):
"""Verify that we fail when users specify --detail and --fields."""
@ -169,7 +168,7 @@ class TestDoCellList(base.TestShellCommandUsingPrintList):
autopaginate=False,
marker=None,
)
self.assertSortedFieldsEqualTo(['id', 'name', 'note'])
self.assertFieldsEqualTo(['id', 'name', 'note'])
def test_invalid_fields(self):
"""Verify that we error out with invalid fields."""

View File

@ -196,7 +196,7 @@ class TestDoCloudList(base.TestShellCommandUsingPrintList):
"""Generate the default argument list for cloud-list."""
kwargs.setdefault('detail', False)
kwargs.setdefault('limit', None)
kwargs.setdefault('fields', [])
kwargs.setdefault('fields', clouds_shell.DEFAULT_CLOUD_FIELDS)
kwargs.setdefault('marker', None)
kwargs.setdefault('all', False)
return super(TestDoCloudList, self).args_for(**kwargs)
@ -206,7 +206,7 @@ class TestDoCloudList(base.TestShellCommandUsingPrintList):
args = self.args_for()
clouds_shell.do_cloud_list(self.craton_client, args)
self.assertSortedFieldsEqualTo(['id', 'name'])
self.assertFieldsEqualTo(clouds_shell.DEFAULT_CLOUD_FIELDS)
def test_negative_limit(self):
"""Ensure we raise an exception for negative limits."""
@ -222,13 +222,13 @@ class TestDoCloudList(base.TestShellCommandUsingPrintList):
marker=None,
autopaginate=False,
)
self.assertSortedFieldsEqualTo(['id', 'name'])
self.assertFieldsEqualTo(clouds_shell.DEFAULT_CLOUD_FIELDS)
def test_fields(self):
"""Verify that we print out specific fields."""
args = self.args_for(fields=['id', 'name', 'note'])
clouds_shell.do_cloud_list(self.craton_client, args)
self.assertSortedFieldsEqualTo(['id', 'name', 'note'])
self.assertFieldsEqualTo(['id', 'name', 'note'])
def test_invalid_fields(self):
"""Verify that we error out with invalid fields."""

View File

@ -46,7 +46,7 @@ class TestDoHostList(base.TestShellCommandUsingPrintList):
kwargs.setdefault('limit', None)
kwargs.setdefault('sort_key', None)
kwargs.setdefault('sort_dir', 'asc')
kwargs.setdefault('fields', [])
kwargs.setdefault('fields', hosts_shell.DEFAULT_HOST_FIELDS)
kwargs.setdefault('marker', None)
kwargs.setdefault('all', False)
kwargs.setdefault('vars', None)
@ -67,9 +67,7 @@ class TestDoHostList(base.TestShellCommandUsingPrintList):
autopaginate=False,
marker=None,
)
self.assertSortedPrintListFieldsEqualTo([
'active', 'cell_id', 'device_type', 'id', 'name'
])
self.assertFieldsEqualTo(hosts_shell.DEFAULT_HOST_FIELDS)
def test_with_cell_id(self):
"""Verify that we include the cell_id in the params."""
@ -84,9 +82,7 @@ class TestDoHostList(base.TestShellCommandUsingPrintList):
autopaginate=False,
marker=None,
)
self.assertSortedPrintListFieldsEqualTo([
'active', 'cell_id', 'device_type', 'id', 'name',
])
self.assertFieldsEqualTo(hosts_shell.DEFAULT_HOST_FIELDS)
def test_with_cloud_id(self):
"""Verify that we include the cell_id in the params."""
@ -101,9 +97,7 @@ class TestDoHostList(base.TestShellCommandUsingPrintList):
autopaginate=False,
marker=None,
)
self.assertSortedPrintListFieldsEqualTo([
'active', 'cell_id', 'device_type', 'id', 'name',
])
self.assertFieldsEqualTo(hosts_shell.DEFAULT_HOST_FIELDS)
def test_with_detail(self):
"""Verify the behaviour of specifying --detail."""
@ -118,22 +112,7 @@ class TestDoHostList(base.TestShellCommandUsingPrintList):
autopaginate=False,
marker=None,
)
self.assertSortedPrintListFieldsEqualTo([
'active',
'cell_id',
'cloud_id',
'created_at',
'device_type',
'id',
'ip_address',
'labels',
'name',
'note',
'parent_id',
'project_id',
'region_id',
'updated_at',
])
self.assertFieldsEqualTo(hosts_shell.HOST_FIELDS)
def test_with_limit(self):
"""Verify the behaviour with --limit specified."""
@ -148,9 +127,7 @@ class TestDoHostList(base.TestShellCommandUsingPrintList):
autopaginate=False,
marker=None,
)
self.assertSortedPrintListFieldsEqualTo([
'active', 'cell_id', 'device_type', 'id', 'name'
])
self.assertFieldsEqualTo(hosts_shell.DEFAULT_HOST_FIELDS)
def test_negative_limit_raises_command_error(self):
"""Verify that we forbid negative limit values."""
@ -172,9 +149,7 @@ class TestDoHostList(base.TestShellCommandUsingPrintList):
autopaginate=False,
marker=None,
)
self.assertSortedPrintListFieldsEqualTo([
'active', 'cell_id', 'device_type', 'id', 'name'
])
self.assertFieldsEqualTo(hosts_shell.DEFAULT_HOST_FIELDS)
def test_with_label(self):
"""Verify the behaviour with --label specified."""
@ -189,9 +164,7 @@ class TestDoHostList(base.TestShellCommandUsingPrintList):
autopaginate=False,
marker=None,
)
self.assertSortedPrintListFieldsEqualTo([
'active', 'cell_id', 'device_type', 'id', 'name'
])
self.assertFieldsEqualTo(hosts_shell.DEFAULT_HOST_FIELDS)
def test_with_device_type(self):
"""Verify the behaviour with --device-type specified."""
@ -206,9 +179,7 @@ class TestDoHostList(base.TestShellCommandUsingPrintList):
autopaginate=False,
marker=None,
)
self.assertSortedPrintListFieldsEqualTo([
'active', 'cell_id', 'device_type', 'id', 'name'
])
self.assertFieldsEqualTo(hosts_shell.DEFAULT_HOST_FIELDS)
def test_with_ip(self):
"""Verify the behaviour with --ip specified."""
@ -223,9 +194,7 @@ class TestDoHostList(base.TestShellCommandUsingPrintList):
autopaginate=False,
marker=None,
)
self.assertSortedPrintListFieldsEqualTo([
'active', 'cell_id', 'device_type', 'id', 'name'
])
self.assertFieldsEqualTo(hosts_shell.DEFAULT_HOST_FIELDS)
def test_fields(self):
"""Verify that we can specify custom fields."""

View File

@ -19,7 +19,6 @@ import mock
from cratonclient import exceptions
from cratonclient.shell.v1 import projects_shell
from cratonclient.tests.unit.shell import base
from cratonclient.v1 import projects
class TestDoShellShow(base.TestShellCommandUsingPrintDict):
@ -53,7 +52,7 @@ class TestDoProjectList(base.TestShellCommandUsingPrintList):
kwargs.setdefault('name', None)
kwargs.setdefault('limit', None)
kwargs.setdefault('detail', False)
kwargs.setdefault('fields', [])
kwargs.setdefault('fields', projects_shell.DEFAULT_PROJECT_FIELDS)
kwargs.setdefault('marker', None)
kwargs.setdefault('all', False)
return super(TestDoProjectList, self).args_for(**kwargs)
@ -68,7 +67,7 @@ class TestDoProjectList(base.TestShellCommandUsingPrintList):
marker=None,
autopaginate=False,
)
self.assertSortedFieldsEqualTo(['id', 'name'])
self.assertFieldsEqualTo(projects_shell.DEFAULT_PROJECT_FIELDS)
def test_negative_limit(self):
"""Ensure we raise an exception for negative limits."""
@ -89,7 +88,7 @@ class TestDoProjectList(base.TestShellCommandUsingPrintList):
autopaginate=False,
marker=None,
)
self.assertSortedFieldsEqualTo(['id', 'name'])
self.assertFieldsEqualTo(projects_shell.DEFAULT_PROJECT_FIELDS)
def test_detail(self):
"""Verify the behaviour of specifying --detail."""
@ -101,7 +100,7 @@ class TestDoProjectList(base.TestShellCommandUsingPrintList):
marker=None,
autopaginate=False,
)
self.assertSortedFieldsEqualTo(sorted(list(projects.PROJECT_FIELDS)))
self.assertFieldsEqualTo(projects_shell.PROJECT_FIELDS)
def test_list_name(self):
"""Verify the behaviour of specifying --detail."""
@ -114,13 +113,13 @@ class TestDoProjectList(base.TestShellCommandUsingPrintList):
autopaginate=False,
marker=None,
)
self.assertSortedFieldsEqualTo(['id', 'name'])
self.assertFieldsEqualTo(projects_shell.DEFAULT_PROJECT_FIELDS)
def test_raises_exception_with_detail_and_fields(self):
"""Verify that we fail when users specify --detail and --fields."""
args = self.args_for(
detail=True,
fields=['id', 'name'],
fields=['name', 'id'],
)
self.assertRaisesCommandErrorWith(projects_shell.do_project_list, args)
@ -128,7 +127,7 @@ class TestDoProjectList(base.TestShellCommandUsingPrintList):
def test_fields(self):
"""Verify that we print out specific fields."""
args = self.args_for(fields=['id', 'name'])
args = self.args_for(fields=['name', 'id'])
projects_shell.do_project_list(self.craton_client, args)
@ -136,7 +135,7 @@ class TestDoProjectList(base.TestShellCommandUsingPrintList):
autopaginate=False,
marker=None,
)
self.assertSortedFieldsEqualTo(['id', 'name'])
self.assertFieldsEqualTo(['name', 'id'])
def test_invalid_fields(self):
"""Verify that we error out with invalid fields."""

View File

@ -204,7 +204,7 @@ class TestDoRegionList(base.TestShellCommandUsingPrintList):
kwargs.setdefault('detail', False)
kwargs.setdefault('cloud', None)
kwargs.setdefault('limit', None)
kwargs.setdefault('fields', [])
kwargs.setdefault('fields', regions_shell.DEFAULT_REGION_FIELDS)
kwargs.setdefault('marker', None)
kwargs.setdefault('all', False)
return super(TestDoRegionList, self).args_for(**kwargs)
@ -214,7 +214,7 @@ class TestDoRegionList(base.TestShellCommandUsingPrintList):
args = self.args_for()
regions_shell.do_region_list(self.craton_client, args)
self.assertSortedFieldsEqualTo(['id', 'name'])
self.assertFieldsEqualTo(regions_shell.DEFAULT_REGION_FIELDS)
def test_with_cloud_id(self):
"""Test region-list with default values."""
@ -225,7 +225,7 @@ class TestDoRegionList(base.TestShellCommandUsingPrintList):
marker=None,
autopaginate=False,
)
self.assertSortedFieldsEqualTo(['id', 'name'])
self.assertFieldsEqualTo(regions_shell.DEFAULT_REGION_FIELDS)
def test_negative_limit(self):
"""Ensure we raise an exception for negative limits."""
@ -241,13 +241,13 @@ class TestDoRegionList(base.TestShellCommandUsingPrintList):
marker=None,
autopaginate=False,
)
self.assertSortedFieldsEqualTo(['id', 'name'])
self.assertFieldsEqualTo(regions_shell.DEFAULT_REGION_FIELDS)
def test_fields(self):
"""Verify that we print out specific fields."""
args = self.args_for(fields=['id', 'name', 'note'])
args = self.args_for(fields=['name', 'id', 'note'])
regions_shell.do_region_list(self.craton_client, args)
self.assertSortedFieldsEqualTo(['id', 'name', 'note'])
self.assertFieldsEqualTo(['name', 'id', 'note'])
def test_invalid_fields(self):
"""Verify that we error out with invalid fields."""

View File

@ -27,15 +27,3 @@ class CellManager(crud.CRUDClient):
key = 'cell'
base_path = '/cells'
resource_class = Cell
CELL_FIELDS = {
'id': 'ID',
'region_id': 'Region ID',
'project_id': 'Project ID',
'cloud_id': 'Cloud ID',
'name': 'Name',
'note': 'Note',
'created_at': 'Created At',
'updated_at': 'Updated At'
}

View File

@ -27,12 +27,3 @@ class CloudManager(crud.CRUDClient):
key = 'cloud'
base_path = '/clouds'
resource_class = Cloud
CLOUD_FIELDS = {
'id': 'ID',
'project_id': 'Project ID',
'name': 'Name',
'note': 'Note',
'created_at': 'Created At',
'updated_at': 'Updated At'
}

View File

@ -27,21 +27,3 @@ class HostManager(crud.CRUDClient):
key = 'host'
base_path = '/hosts'
resource_class = Host
HOST_FIELDS = {
'id': 'ID',
'name': 'Name',
'device_type': 'Device Type',
'project_id': 'Project ID',
'cloud_id': 'Cloud ID',
'region_id': 'Region ID',
'cell_id': 'Cell ID',
'ip_address': 'IP Address',
'active': 'Active',
'note': 'Note',
'created_at': 'Created At',
'updated_at': 'Updated At',
'labels': 'Labels',
'parent_id': 'Parent ID',
}

View File

@ -27,11 +27,3 @@ class ProjectManager(crud.CRUDClient):
key = 'project'
base_path = '/projects'
resource_class = Project
PROJECT_FIELDS = {
'id': 'ID',
'name': 'Name',
'created_at': 'Created At',
'updated_at': 'Updated At'
}

View File

@ -28,13 +28,3 @@ class RegionManager(crud.CRUDClient):
base_path = '/regions'
resource_class = Region
project_id = 0
REGION_FIELDS = {
'id': 'ID',
'project_id': 'Project ID',
'cloud_id': 'Cloud ID',
'name': 'Name',
'note': 'Note',
'created_at': 'Created At',
'updated_at': 'Updated At'
}