diff --git a/.zuul.yaml b/.zuul.yaml index e14019c..399f4db 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -40,12 +40,14 @@ check: jobs: - openstack-tox-pep8 + - openstack-tox-py27 - kollacli-tox-mypy - kollacli-tox-functional - kollacli-tox-functional-py35 gate: jobs: - openstack-tox-pep8 + - openstack-tox-py27 - kollacli-tox-mypy - kollacli-tox-functional - kollacli-tox-functional-py35 diff --git a/kolla_cli/commands/host.py b/kolla_cli/commands/host.py index 70c70f6..9ac9e47 100644 --- a/kolla_cli/commands/host.py +++ b/kolla_cli/commands/host.py @@ -90,15 +90,10 @@ class HostDestroy(Command): if parsed_args.removeimages: remove_images = True - if include_data: - question = ('This will delete all containers and data' - ', are you sure? (y/n)') - answer = raw_input(question) - while answer != 'y' and answer != 'n': - answer = raw_input(question) - if answer is 'n': - LOG.info('Aborting destroy') - return + if include_data and not self._is_ok_to_delete_data(): + LOG.info('Aborting destroy') + return + verbose_level = self.app.options.verbose_level job = CLIENT.host_destroy(hostnames, destroy_type, @@ -123,6 +118,14 @@ class HostDestroy(Command): except Exception as e: raise Exception(traceback.format_exc()) + def _is_ok_to_delete_data(self): + question = ('This will delete all containers and data' + ', are you sure? (y/n)') + answer = raw_input(question) + while answer != 'y' and answer != 'n': + answer = raw_input(question) + return True if answer == 'y' else False + class HostRemove(Command): """Remove host from openstack-kolla.""" @@ -231,7 +234,7 @@ class HostCheck(Command): status = u._('success') msg = '' if not info['success']: - status = u._('failed- ') + status = u._('failed-') msg = info['msg'] all_ok = False LOG.info(u._('Host {host}: {sts} {msg}') @@ -280,7 +283,7 @@ class HostSetup(Command): LOG.info( u._LI('Skipping setup of host ({host}) as ' 'ssh check is ok.').format(host=hostname)) - return True + return 0 if parsed_args.insecure: password = parsed_args.insecure.strip() diff --git a/kolla_cli/shell.py b/kolla_cli/shell.py index e7c4117..ad70015 100755 --- a/kolla_cli/shell.py +++ b/kolla_cli/shell.py @@ -41,7 +41,7 @@ class KollaCli(App): inventory_path = os.path.join(get_kolla_cli_etc(), INVENTORY_PATH) - if os.path.isfile(inventory_path) is False: + if not self._is_inventory_present(inventory_path): err_string = u._( 'Required file ({inventory}) does not exist.\n' 'Please re-install the kollacli to ' @@ -57,6 +57,9 @@ class KollaCli(App): self.dump_stack_trace = False + def _is_inventory_present(self, inventory_path): + return os.path.isfile(inventory_path) + def main(argv=sys.argv[1:]): shell = KollaCli() diff --git a/kolla_cli/tests/unit/common.py b/kolla_cli/tests/unit/common.py new file mode 100644 index 0000000..49e8f52 --- /dev/null +++ b/kolla_cli/tests/unit/common.py @@ -0,0 +1,32 @@ +# Copyright (c) 2018 OpenStack Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +import testtools + +from kolla_cli.api.host import Host +from kolla_cli.common.ansible.job import AnsibleJob +from kolla_cli import shell + + +class KollaCliUnitTest(testtools.TestCase): + + def run_cli_command(self, command_string): + # return 0 if command succeeded, non-0 if failed + args = command_string.split() + return shell.main(args) + + def get_fake_job(self): + return AnsibleJob(None, None, None, None) + + def get_fake_host(self, hostname='foo'): + return Host(hostname) diff --git a/kolla_cli/tests/unit/test_host_cmd.py b/kolla_cli/tests/unit/test_host_cmd.py new file mode 100644 index 0000000..0389aae --- /dev/null +++ b/kolla_cli/tests/unit/test_host_cmd.py @@ -0,0 +1,225 @@ +# Copyright (c) 2018 OpenStack Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +import mock + +from kolla_cli.tests.unit.common import KollaCliUnitTest + + +class TestUnit(KollaCliUnitTest): + @mock.patch('kolla_cli.api.client.ClientApi.host_add') + @mock.patch('kolla_cli.shell.KollaCli._is_inventory_present', + return_value=True) + def test_host_add(self, _, mock_add): + hostname = 'foo' + ret = self.run_cli_command('host add %s' % hostname) + self.assertEqual(ret, 0) + mock_add.assert_called_once_with([hostname]) + + @mock.patch('kolla_cli.api.client.ClientApi.host_remove') + @mock.patch('kolla_cli.shell.KollaCli._is_inventory_present', + return_value=True) + def test_host_remove(self, _, mock_remove): + hostname = 'foo' + ret = self.run_cli_command('host remove %s' % hostname) + self.assertEqual(ret, 0) + mock_remove.assert_called_once_with([hostname]) + + @mock.patch('kolla_cli.api.client.ClientApi.host_get_all') + @mock.patch('kolla_cli.api.client.ClientApi.host_get') + @mock.patch('kolla_cli.shell.KollaCli._is_inventory_present', + return_value=True) + def test_host_list(self, _, mock_get, mock_get_all): + # get all hosts + mock_get_all.return_value = [] + ret = self.run_cli_command('host list') + self.assertEqual(ret, 0) + mock_get_all.assert_called_once_with() + + # get a specific host + hostname = 'foo' + mock_get.return_value = [] + ret = self.run_cli_command('host list %s' % hostname) + self.assertEqual(ret, 0) + mock_get.assert_called_once_with([hostname]) + + @mock.patch('kolla_cli.commands.host.HostDestroy._is_ok_to_delete_data', + return_value='y') + @mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status') + @mock.patch('kolla_cli.api.client.ClientApi.host_get_all') + @mock.patch('kolla_cli.api.client.ClientApi.host_destroy') + @mock.patch('kolla_cli.shell.KollaCli._is_inventory_present', + return_value=True) + def test_host_destroy(self, _, mock_destroy, mock_get_all, + mock_get_status, mock_prompt): + hostname = 'foo' + mock_get_all.return_value = [self.get_fake_host(hostname)] + mock_destroy.return_value = self.get_fake_job() + mock_get_status.return_value = 0 + + # default destroy hostname + ret = self.run_cli_command('host destroy %s' % hostname) + self.assertEqual(ret, 0) + mock_destroy.assert_called_once_with([hostname], 'kill', 1, + False, False) + # destroy all + mock_destroy.reset_mock() + ret = self.run_cli_command('host destroy all') + self.assertEqual(ret, 0) + mock_destroy.assert_called_once_with([hostname], 'kill', 1, + False, False) + # destroy --stop + mock_destroy.reset_mock() + ret = self.run_cli_command('host destroy %s --stop' % hostname) + self.assertEqual(ret, 0) + mock_destroy.assert_called_once_with([hostname], 'stop', 1, + False, False) + # destroy --includedata + mock_destroy.reset_mock() + ret = self.run_cli_command('host destroy %s --includedata' % hostname) + self.assertEqual(ret, 0) + mock_destroy.assert_called_once_with([hostname], 'kill', 1, + True, False) + + # destroy --removeimages + mock_destroy.reset_mock() + ret = self.run_cli_command('host destroy %s --removeimages' + % hostname) + self.assertEqual(ret, 0) + mock_destroy.assert_called_once_with([hostname], 'kill', 1, + False, True) + + @mock.patch('kolla_cli.commands.host.LOG.info') + @mock.patch('kolla_cli.api.client.ClientApi.host_get_all') + @mock.patch('kolla_cli.api.client.ClientApi.host_ssh_check') + @mock.patch('kolla_cli.shell.KollaCli._is_inventory_present', + return_value=True) + def test_host_ssh_check(self, _, mock_ssh_check, mock_get_all, mock_log): + hostname = 'foo' + check_ok_response = {hostname: {'success': True}} + check_bad_response = {hostname: {'success': False, 'msg': 'FAILED'}} + mock_get_all.return_value = [self.get_fake_host(hostname)] + + # host check hostname (success) + mock_ssh_check.return_value = check_ok_response + ret = self.run_cli_command('host check %s' % hostname) + self.assertEqual(ret, 0) + mock_ssh_check.assert_called_once_with([hostname]) + mock_log.assert_called_once_with('Host %s: success ' % hostname) + + # host check all (success) + mock_ssh_check.reset_mock() + mock_log.reset_mock() + mock_ssh_check.return_value = check_ok_response + ret = self.run_cli_command('host check all') + self.assertEqual(ret, 0) + mock_ssh_check.assert_called_once_with([hostname]) + mock_log.assert_called_once_with('Host %s: success ' % hostname) + + # host check hostname (fail) + mock_ssh_check.reset_mock() + mock_log.reset_mock() + mock_ssh_check.return_value = check_bad_response + ret = self.run_cli_command('host check %s' % hostname) + self.assertEqual(ret, 1) + mock_ssh_check.assert_called_once_with([hostname]) + mock_log.assert_called_once_with('Host %s: failed- FAILED' % hostname) + + @mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status') + @mock.patch('kolla_cli.commands.host.ClientApi.host_precheck') + @mock.patch('kolla_cli.api.client.ClientApi.host_get_all') + @mock.patch('kolla_cli.shell.KollaCli._is_inventory_present', + return_value=True) + def test_host_precheck(self, _, mock_get_all, mock_precheck, + mock_get_status): + hostname = 'foo' + mock_get_all.return_value = [self.get_fake_host(hostname)] + + # host check hostname --predeploy (success) + mock_precheck.return_value = self.get_fake_job() + mock_get_status.return_value = 0 + ret = self.run_cli_command('host check %s --predeploy' % hostname) + self.assertEqual(ret, 0) + mock_precheck.assert_called_once_with([hostname], 1) + + # host check hostname --predeploy (fail) + mock_precheck.reset_mock() + mock_get_status.return_value = 1 + ret = self.run_cli_command('host check %s --predeploy' % hostname) + self.assertEqual(ret, 1) + mock_precheck.assert_called_once_with([hostname], 1) + + @mock.patch('kolla_cli.commands.host.HostSetup._get_yml_data') + @mock.patch('getpass.getpass') + @mock.patch('kolla_cli.commands.host.ClientApi.host_ssh_check') + @mock.patch('kolla_cli.commands.host.ClientApi.host_setup') + @mock.patch('kolla_cli.api.client.ClientApi.host_get_all') + @mock.patch('kolla_cli.shell.KollaCli._is_inventory_present', + return_value=True) + def test_host_setup(self, _, mock_get_all, mock_setup, mock_ssh_check, + mock_passwd, mock_yml): + password = 'PASSWORD' + hostname = 'foo' + mock_get_all.return_value = [self.get_fake_host(hostname)] + mock_passwd.return_value = password + + # single host setup (host not yet setup) + mock_ssh_check.return_value = {hostname: {'success': False}} + ret = self.run_cli_command('host setup %s' % hostname) + self.assertEqual(ret, 0) + mock_ssh_check.assert_called_once_with([hostname]) + mock_setup.assert_called_once_with({hostname: {'password': password}}) + + # single host setup --insecure (host already setup) + mock_ssh_check.reset_mock() + mock_setup.reset_mock() + mock_ssh_check.return_value = {hostname: {'success': True}} + ret = self.run_cli_command('host setup %s --insecure %s' + % (hostname, password)) + self.assertEqual(ret, 0) + mock_ssh_check.assert_called_once_with([hostname]) + mock_setup.assert_not_called() + + # multi-host setup + mock_ssh_check.reset_mock() + mock_setup.reset_mock() + fake_path = '/bogus' + mock_yml.return_value = {hostname: {'password': password}} + ret = self.run_cli_command('host setup --file %s' % fake_path) + self.assertEqual(ret, 0) + mock_setup.assert_called_once_with({hostname: {'password': password}}) + mock_yml.assert_called_once_with(fake_path) + mock_ssh_check.assert_not_called() + + @mock.patch('kolla_cli.common.ansible.job.AnsibleJob.get_status') + @mock.patch('kolla_cli.api.client.ClientApi.host_get_all') + @mock.patch('kolla_cli.api.client.ClientApi.host_stop') + @mock.patch('kolla_cli.shell.KollaCli._is_inventory_present', + return_value=True) + def test_host_stop(self, _, mock_stop, mock_get_all, + mock_get_status): + hostname = 'foo' + mock_get_all.return_value = [self.get_fake_host(hostname)] + mock_get_status.return_value = 0 + mock_stop.return_value = self.get_fake_job() + + # host stop hostname + ret = self.run_cli_command('host stop %s' % hostname) + self.assertEqual(ret, 0) + mock_stop.assert_called_once_with([hostname], 1) + + # host stop all + mock_stop.reset_mock() + ret = self.run_cli_command('host stop all') + self.assertEqual(ret, 0) + mock_stop.assert_called_once_with([hostname], 1) diff --git a/tox.ini b/tox.ini index 776fd4b..87f7547 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] minversion = 1.6 skipsdist = True -envlist = pep8,mypy,functional,functional-py35 +envlist = pep8,mypy,functional,functional-py35,py27 [testenv] usedevelop=True @@ -17,6 +17,9 @@ commands = find . -type f -name "*.py[c|o]" -delete find . -type d -name "__pycache__" -delete +[testenv:py27] +commands = ostestr {posargs} + [testenv:functional] whitelist_externals = {[testenv]whitelist_externals}