Added optional arguments to host-list command and unit tests.

Implemented handling of argument values as well as defined
acceptable field values for hosts.

Change-Id: I2fc2267d402a87e09c059dfdc8b10d86e8eeb69c
This commit is contained in:
Chris Spencer
2016-07-28 12:33:55 -07:00
parent d4f0c1ccb8
commit 052caea612
4 changed files with 188 additions and 2 deletions

View File

@@ -75,6 +75,12 @@ class HTTPError(ClientException):
self.status_code = code
class CommandError(ClientException):
"""Client command was invalid or failed."""
message = "The command used was invalid or caused an error."""
class ConnectionFailed(HTTPError):
"""Connecting to the server failed."""

View File

@@ -13,11 +13,73 @@
# under the License.
"""Hosts resource and resource shell wrapper."""
from cratonclient.common import cliutils
from cratonclient import exceptions as exc
from cratonclient.v1.hosts import HOST_FIELDS as h_fields
@cliutils.arg('-c', '--cell',
metavar='<cell>',
type=int,
help='Integer ID of the cell that contains '
'the desired list of hosts.')
@cliutils.arg('--detail',
action='store_true',
default=False,
help='Show detailed information about the hosts.')
@cliutils.arg('--limit',
metavar='<limit>',
type=int,
help='Maximum number of hosts to return.')
@cliutils.arg('--sort-key',
metavar='<field>',
help='Host field that will be used for sorting.')
@cliutils.arg('--sort-dir',
metavar='<direction>',
default='asc',
help='Sort direction: "asc" (default) or "desc".')
@cliutils.arg('--fields',
nargs='+',
metavar='<fields>',
default=[],
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')
def do_host_list(cc, args):
"""Print list of hosts which are registered with the Craton service."""
params = {}
columns = ['id', 'name']
default_fields = ['id', 'name', 'type', 'active', 'cell_id']
if args.cell is not None:
params['cell'] = args.cell
if args.limit is not None:
if args.limit < 0:
raise exc.CommandError('Invalid limit specified. Expected '
'non-negative limit, got {0}'
.format(args.limit))
params['limit'] = args.limit
if args.detail:
fields = h_fields
params['detail'] = args.detail
elif args.fields:
fields = {x: h_fields[x] for x in args.fields}
else:
fields = {x: h_fields[x] for x in default_fields}
if args.sort_key is not None:
fields_map = dict(zip(fields.keys(), fields.keys()))
# TODO(cmspence): Do we want to allow sorting by field heading value?
try:
sort_key = fields_map[args.sort_key]
except KeyError:
raise exc.CommandError(
'{0} is an invalid key for sorting, valid values for '
'--sort-key are: {1}'.format(args.sort_key, h_fields.keys())
)
params['sort_key'] = sort_key
if args.sort_dir is not None:
if args.sort_dir not in ('asc', 'desc'):
raise exc.CommandError('Invalid sort direction specified. The '
'expected valid values for --sort-dir '
'are: "asc", "desc".')
params['sort_dir'] = args.sort_dir
hosts = cc.hosts.list(args.craton_project_id, **params)
cliutils.print_list(hosts, columns)
cliutils.print_list(hosts, list(fields))

View File

@@ -14,6 +14,7 @@
import mock
from cratonclient import exceptions as exc
from cratonclient.tests import base
@@ -25,3 +26,105 @@ class TestHostsShell(base.ShellTestCase):
"""Verify that no arguments prints out all project hosts."""
self.shell('host-list')
self.assertTrue(mock_list.called)
@mock.patch('cratonclient.v1.hosts.HostManager.list')
def test_host_list_parse_param_success(self, mock_list):
"""Verify that success of parsing a subcommand argument."""
self.shell('host-list --limit 0')
self.assertTrue(mock_list.called)
@mock.patch('cratonclient.v1.hosts.HostManager.list')
def test_host_list_limit_0_succcess(self, mock_list):
"""Verify that --limit 0 prints out all project hosts."""
self.shell('host-list --limit 0')
mock_list.assert_called_once_with(mock.ANY, limit=0)
@mock.patch('cratonclient.v1.hosts.HostManager.list')
def test_host_list_limit_positive_num_success(self, mock_list):
"""Verify --limit X, where X is a positive integer, succeeds.
The command will print out X number of project hosts.
"""
self.shell('host-list --limit 1')
mock_list.assert_called_once_with(mock.ANY, limit=1)
def test_host_list_limit_negative_num_failure(self):
"""Verify --limit X, where X is a negative integer, fails.
The command will cause a Command Error message response.
"""
self.assertRaises(exc.CommandError,
self.shell,
'host-list --limit -1')
@mock.patch('cratonclient.v1.hosts.HostManager.list')
def test_host_list_cell_success(self, mock_list):
"""Verify --cell arguments successfully pass cell to Client."""
for cell_arg in ['-c', '--cell']:
self.shell('host-list {0} 1'.format(cell_arg))
mock_list.assert_called_once_with(mock.ANY, cell=1)
mock_list.reset_mock()
@mock.patch('cratonclient.v1.hosts.HostManager.list')
def test_host_list_detail_success(self, mock_list):
"""Verify --detail argument successfully pass detail to Client."""
self.shell('host-list --detail')
mock_list.assert_called_once_with(mock.ANY, detail=True)
@mock.patch('cratonclient.v1.hosts.HostManager.list')
@mock.patch('cratonclient.common.cliutils.print_list')
def test_host_list_fields_success(self, mock_printlist, mock_list):
"""Verify --fields argument successfully passed to Client."""
self.shell('host-list --fields id name')
mock_list.assert_called_once_with(mock.ANY)
mock_printlist.assert_called_once_with(mock.ANY,
list({'id': 'ID',
'name': 'Name'}))
@mock.patch('cratonclient.v1.hosts.HostManager.list')
def test_host_list_detail_and_fields_specified(self, mock_list):
"""Verify --fields ignored when --detail argument passed in."""
self.shell('host-list --fields id name --detail')
mock_list.assert_called_once_with(mock.ANY, detail=True)
@mock.patch('cratonclient.v1.hosts.HostManager.list')
def test_host_list_sort_key_field_key_success(self, mock_list):
"""Verify --sort-key arguments successfully passed to Client."""
self.shell('host-list --sort-key cell_id')
mock_list.assert_called_once_with(mock.ANY,
sort_key='cell_id',
sort_dir='asc')
def test_host_list_sort_key_invalid(self):
"""Verify --sort-key with invalid args, fails with Command Error."""
self.assertRaises(exc.CommandError,
self.shell,
'host-list --sort-key invalid')
@mock.patch('cratonclient.v1.hosts.HostManager.list')
def test_host_list_sort_dir_not_passed_without_sort_key(self, mock_list):
"""Verify --sort-dir arg ignored without --sort-key."""
self.shell('host-list --sort-dir desc')
mock_list.assert_called_once_with(mock.ANY)
@mock.patch('cratonclient.v1.hosts.HostManager.list')
def test_host_list_sort_dir_asc_success(self, mock_list):
"""Verify --sort-dir asc successfully passed to Client."""
self.shell('host-list --sort-key name --sort-dir asc')
mock_list.assert_called_once_with(mock.ANY,
sort_key='name',
sort_dir='asc')
@mock.patch('cratonclient.v1.hosts.HostManager.list')
def test_host_list_sort_dir_desc_success(self, mock_list):
"""Verify --sort-dir desc successfully passed to Client."""
self.shell('host-list --sort-key name --sort-dir desc')
mock_list.assert_called_once_with(mock.ANY,
sort_key='name',
sort_dir='desc')
def test_host_list_sort_dir_invalid_value(self):
"""Verify --sort-dir with invalid args, fails with Command Error."""
self.assertRaises(exc.CommandError,
self.shell,
'host-list --sort-key name --sort-dir invalid')

View File

@@ -32,3 +32,18 @@ class HostManager(crud.CRUDClient):
"""Retrieve the hosts in a specific region."""
kwargs['project'] = str(project_id)
super(HostManager, self).list(**kwargs)
HOST_FIELDS = {
'id': 'ID',
'name': 'Name',
'type': 'Type',
'project_id': 'Project ID',
'region_id': 'Region ID',
'cell_id': 'Cell ID',
'ip_address': 'IP Address',
'active': 'Active',
'note': 'Note',
'access_secret_id': "Access Secret ID",
'created_at': 'Created At',
'update_at': 'Updated At'
}