Adding support for creating a host.
Adding ability to create a host in both python and cli clients Adding unit tests for verifying error message on incorrect args and verifying host created. Implements: blueprint craton-client-access-inventory (partial) Closes-Bug: #1607843 Change-Id: I61dbe53392a4f3c00ad50eec774e8844cd2c864d
This commit is contained in:
parent
052caea612
commit
8b4463d24d
@ -12,9 +12,11 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""Craton CLI helper classes and functions."""
|
||||
import json
|
||||
import os
|
||||
import prettytable
|
||||
import six
|
||||
import textwrap
|
||||
|
||||
from oslo_utils import encodeutils
|
||||
|
||||
@ -97,6 +99,44 @@ def print_list(objs, fields, formatters=None, sortby_index=0,
|
||||
print(encodeutils.safe_encode(pt.get_string(**kwargs)))
|
||||
|
||||
|
||||
def print_dict(dct, dict_property="Property", wrap=0, dict_value='Value',
|
||||
json_flag=False):
|
||||
"""Print a `dict` as a table of two columns.
|
||||
|
||||
:param dct: `dict` to print
|
||||
:param dict_property: name of the first column
|
||||
:param wrap: wrapping for the second column
|
||||
:param dict_value: header label for the value (second) column
|
||||
:param json_flag: print `dict` as JSON instead of table
|
||||
"""
|
||||
if json_flag:
|
||||
print(json.dumps(dct, indent=4, separators=(',', ': ')))
|
||||
return
|
||||
pt = prettytable.PrettyTable([dict_property, dict_value])
|
||||
pt.align = 'l'
|
||||
for k, v in sorted(dct.items()):
|
||||
# convert dict to str to check length
|
||||
if isinstance(v, dict):
|
||||
v = six.text_type(v)
|
||||
if wrap > 0:
|
||||
v = textwrap.fill(six.text_type(v), wrap)
|
||||
# if value has a newline, add in multiple rows
|
||||
# e.g. fault with stacktrace
|
||||
if v and isinstance(v, six.string_types) and r'\n' in v:
|
||||
lines = v.strip().split(r'\n')
|
||||
col1 = k
|
||||
for line in lines:
|
||||
pt.add_row([col1, line])
|
||||
col1 = ''
|
||||
else:
|
||||
pt.add_row([k, v])
|
||||
|
||||
if six.PY3:
|
||||
print(encodeutils.safe_encode(pt.get_string()).decode())
|
||||
else:
|
||||
print(encodeutils.safe_encode(pt.get_string()))
|
||||
|
||||
|
||||
def env(*args, **kwargs):
|
||||
"""Return the first environment variable set.
|
||||
|
||||
|
@ -83,3 +83,54 @@ def do_host_list(cc, args):
|
||||
|
||||
hosts = cc.hosts.list(args.craton_project_id, **params)
|
||||
cliutils.print_list(hosts, list(fields))
|
||||
|
||||
|
||||
@cliutils.arg('-n', '--name',
|
||||
metavar='<name>',
|
||||
required=True,
|
||||
help='Name of the host.')
|
||||
@cliutils.arg('-i', '--ip_address',
|
||||
metavar='<ipaddress>',
|
||||
required=True,
|
||||
help='IP Address of the host.')
|
||||
@cliutils.arg('-p', '--project',
|
||||
dest='project_id',
|
||||
metavar='<project>',
|
||||
type=int,
|
||||
required=True,
|
||||
help='ID of the project that the host belongs to.')
|
||||
@cliutils.arg('-r', '--region',
|
||||
dest='region_id',
|
||||
metavar='<region>',
|
||||
type=int,
|
||||
required=True,
|
||||
help='ID of the region that the host belongs to.')
|
||||
@cliutils.arg('-c', '--cell',
|
||||
dest='cell_id',
|
||||
metavar='<cell>',
|
||||
type=int,
|
||||
help='ID of the cell that the host belongs to.')
|
||||
@cliutils.arg('-a', '--active',
|
||||
default=True,
|
||||
help='Status of the host. Active or inactive.')
|
||||
@cliutils.arg('-t', '--type',
|
||||
help='Type of the host.')
|
||||
@cliutils.arg('--note',
|
||||
help='Note about the host.')
|
||||
@cliutils.arg('--access_secret',
|
||||
type=int,
|
||||
dest='access_secret_id',
|
||||
help='ID of the access secret of the host.')
|
||||
@cliutils.arg('-l', '--labels',
|
||||
default=[],
|
||||
help='List of labels for the host.')
|
||||
def do_host_create(cc, args):
|
||||
"""Register a new host with the Craton service."""
|
||||
host_fields = ['id', 'name', 'type', 'active', 'project_id', 'region_id',
|
||||
'cell_id', 'note', 'access_secret_id', 'ip_address']
|
||||
fields = {k: v for (k, v) in vars(args).items()
|
||||
if k in host_fields and not (v is None)}
|
||||
|
||||
host = cc.hosts.create(**fields)
|
||||
data = {f: getattr(host, f, '') for f in host_fields}
|
||||
cliutils.print_dict(data, wrap=72)
|
||||
|
@ -18,7 +18,6 @@
|
||||
|
||||
import mock
|
||||
import six
|
||||
import sys
|
||||
|
||||
from oslotest import base
|
||||
|
||||
@ -41,6 +40,5 @@ class ShellTestCase(base.BaseTestCase):
|
||||
main_shell = main.CratonShell()
|
||||
main_shell.main(arg_str.split())
|
||||
except SystemExit:
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
self.assertIn(exc_value.code, exitcodes)
|
||||
pass
|
||||
return (mock_stdout.getvalue(), mock_stderr.getvalue())
|
||||
|
@ -13,14 +13,38 @@
|
||||
"""Tests for `cratonclient.shell.v1.hosts_shell` module."""
|
||||
|
||||
import mock
|
||||
import re
|
||||
|
||||
from argparse import Namespace
|
||||
from testtools import matchers
|
||||
|
||||
from cratonclient import exceptions as exc
|
||||
from cratonclient.shell.v1 import hosts_shell
|
||||
from cratonclient.tests import base
|
||||
from cratonclient.v1 import hosts
|
||||
|
||||
|
||||
class TestHostsShell(base.ShellTestCase):
|
||||
"""Test our craton hosts shell commands."""
|
||||
|
||||
re_options = re.DOTALL | re.MULTILINE
|
||||
host_valid_fields = None
|
||||
host_invalid_field = None
|
||||
|
||||
def setUp(self):
|
||||
"""Setup required test fixtures."""
|
||||
super(TestHostsShell, self).setUp()
|
||||
self.host_valid_fields = Namespace(project_id=1,
|
||||
region_id=1,
|
||||
name='mock_host',
|
||||
ip_address='127.0.0.1',
|
||||
active=True)
|
||||
self.host_invalid_field = Namespace(project_id=1, region_id=1,
|
||||
name='mock_host',
|
||||
ip_address='127.0.0.1',
|
||||
active=True,
|
||||
invalid_foo='ignored')
|
||||
|
||||
@mock.patch('cratonclient.v1.hosts.HostManager.list')
|
||||
def test_host_list_success(self, mock_list):
|
||||
"""Verify that no arguments prints out all project hosts."""
|
||||
@ -128,3 +152,31 @@ class TestHostsShell(base.ShellTestCase):
|
||||
self.assertRaises(exc.CommandError,
|
||||
self.shell,
|
||||
'host-list --sort-key name --sort-dir invalid')
|
||||
|
||||
def test_host_create_missing_required_args(self):
|
||||
"""Verify that missing required args results in error message."""
|
||||
expected_responses = [
|
||||
'.*?^usage: craton host-create',
|
||||
'.*?^craton host-create: error:.*$'
|
||||
]
|
||||
stdout, stderr = self.shell('host-create')
|
||||
actual_output = stdout + stderr
|
||||
for r in expected_responses:
|
||||
self.assertThat(actual_output,
|
||||
matchers.MatchesRegex(r, self.re_options))
|
||||
|
||||
@mock.patch('cratonclient.v1.hosts.HostManager.create')
|
||||
def test_do_host_create_calls_host_manager_with_fields(self, mock_create):
|
||||
"""Verify that do host create calls HostManager create."""
|
||||
client = mock.Mock()
|
||||
client.hosts = hosts.HostManager(mock.ANY, 'http://127.0.0.1/')
|
||||
hosts_shell.do_host_create(client, self.host_valid_fields)
|
||||
mock_create.assert_called_once_with(**vars(self.host_valid_fields))
|
||||
|
||||
@mock.patch('cratonclient.v1.hosts.HostManager.create')
|
||||
def test_do_host_create_ignores_unknown_fields(self, mock_create):
|
||||
"""Verify that do host create ignores unknown field."""
|
||||
client = mock.Mock()
|
||||
client.hosts = hosts.HostManager(mock.ANY, 'http://127.0.0.1/')
|
||||
hosts_shell.do_host_create(client, self.host_invalid_field)
|
||||
mock_create.assert_called_once_with(**vars(self.host_valid_fields))
|
||||
|
@ -101,3 +101,17 @@ class TestMainShell(base.ShellTestCase):
|
||||
cratonShellMainMock.side_effect = Exception(mock.Mock(status=404),
|
||||
'some error')
|
||||
self.assertRaises(SystemExit, main.main)
|
||||
|
||||
@mock.patch('cratonclient.shell.v1.hosts_shell.do_host_create')
|
||||
def test_main_routes_sub_command(self, mock_create):
|
||||
"""Verify main shell calls correct subcommand."""
|
||||
url = '--craton-url test_url'
|
||||
username = '--os-username test_name'
|
||||
pw = '--os-password test_pw'
|
||||
proj_id = '--craton-project-id 1'
|
||||
self.shell('{} {} {} {} host-create'.format(url,
|
||||
username,
|
||||
pw,
|
||||
proj_id))
|
||||
|
||||
self.assertTrue(mock_create.called)
|
||||
|
@ -28,10 +28,11 @@ class HostManager(crud.CRUDClient):
|
||||
base_path = '/hosts'
|
||||
resource_class = Host
|
||||
|
||||
def list(self, project_id, **kwargs):
|
||||
def list(self, region_id, **kwargs):
|
||||
"""Retrieve the hosts in a specific region."""
|
||||
kwargs['project'] = str(project_id)
|
||||
super(HostManager, self).list(**kwargs)
|
||||
kwargs['region'] = str(region_id)
|
||||
return super(HostManager, self).list(**kwargs)
|
||||
|
||||
|
||||
HOST_FIELDS = {
|
||||
'id': 'ID',
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
hacking<0.12,>=0.10.0
|
||||
flake8_docstrings==0.2.1.post1 # MIT
|
||||
|
||||
coverage>=3.6
|
||||
python-subunit>=0.0.18
|
||||
sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2
|
||||
|
Loading…
x
Reference in New Issue
Block a user