From f7166ed61c7d2915ea15da88600d6deb313cee92 Mon Sep 17 00:00:00 2001 From: Borne Mace Date: Wed, 2 Nov 2016 15:52:06 -0700 Subject: [PATCH] API support for the reconfigure action Added support for use of the kolla ansible reconfigure action. This should be used after the change of properties / config files to push out the changes and put them into effect in whatever services are effected by the changes. Change-Id: Ief1ceabcad8e9598d106164dabed9fe563eadfbd Jira-Issue: None --- kollacli/api/async.py | 16 +++ kollacli/common/ansible/actions.py | 14 +++ tests/destroy.py | 30 ------ tests/reconfigure.py | 48 +++++++++ tests/stop.py | 160 +++++++++++++++++++++++++++++ 5 files changed, 238 insertions(+), 30 deletions(-) create mode 100644 tests/reconfigure.py create mode 100644 tests/stop.py diff --git a/kollacli/api/async.py b/kollacli/api/async.py index e7bf32a..d0dbe1d 100644 --- a/kollacli/api/async.py +++ b/kollacli/api/async.py @@ -161,3 +161,19 @@ class AsyncApi(object): ansible_job = actions.stop_hosts(hostnames, verbose_level) return Job(ansible_job) + + def async_reconfigure(self, verbose_level=1): + # type: (int) -> Job + """Reconfigure. + + Reconfigure containers on hosts. + + :param verbose_level: the higher the number, the more verbose + :type verbose_level: integer + :return: Job object + :rtype: Job + """ + check_arg(verbose_level, u._('Verbose level'), int) + + ansible_job = actions.reconfigure(verbose_level) + return Job(ansible_job) diff --git a/kollacli/common/ansible/actions.py b/kollacli/common/ansible/actions.py index f1ec76e..d3fc96b 100644 --- a/kollacli/common/ansible/actions.py +++ b/kollacli/common/ansible/actions.py @@ -118,6 +118,20 @@ def stop_hosts(hostnames=[], verbose_level=1): return job +def reconfigure(verbose_level=1): + playbook = AnsiblePlaybook() + kolla_home = get_kolla_home() + playbook.playbook_path = os.path.join(kolla_home, + 'ansible/site.yml') + playbook.extra_vars = 'action=reconfigure' + playbook.verbose_level = verbose_level + + _run_deploy_rules(playbook) + + job = playbook.run() + return job + + def upgrade(verbose_level=1, servicenames=[]): playbook = AnsiblePlaybook() kolla_home = get_kolla_home() diff --git a/tests/destroy.py b/tests/destroy.py index 760eda1..dab1617 100644 --- a/tests/destroy.py +++ b/tests/destroy.py @@ -36,12 +36,6 @@ EXPECTED_CONTAINERS_1 = [ 'rabbitmq' ] -# TODO(bmace) needs to be refactored after destroy change -# after destroy --includedata -# EXPECTED_CONTAINERS_2 = [ -# 'rabbitmq_data' -# ] - class TestFunctional(KollaCliTest): @@ -141,30 +135,6 @@ class TestFunctional(KollaCliTest): 'is not running on host: %s ' % hostname + 'after deploy.') -# TODO(bmace) Invalid until new destroy changes are committed. -# Data containers no longer exist. -# The tests will need to check for data volumes. -# # destroy non-data services (via --stop flag) -# # this should leave only data containers running -# self.log.info('Start destroy #2, do not include data') -# job = CLIENT.async_host_destroy(hostnames, destroy_type='stop', -# include_data=False) -# self._process_job(job, 'destroy #2', is_physical_host) -# -# if is_physical_host: -# docker_ps = test_config.run_remote_cmd('docker ps', hostname) -# for service in CLIENT.service_get_all(): -# if service.name not in ENABLED_SERVICES: -# self.assertNotIn(service.name, docker_ps, -# 'disabled service: %s ' % service.name + -# 'is running on host: %s ' % hostname + -# 'after destroy (no data).') -# for servicename in EXPECTED_CONTAINERS_2: -# self.assertIn(servicename, docker_ps, -# 'enabled service: %s ' % servicename + -# 'is not running on host: %s ' % hostname + -# 'after destroy (no data).') -# self.log.info('Start destroy #3, include data') job = CLIENT.async_host_destroy(hostnames, destroy_type='stop', include_data=True) diff --git a/tests/reconfigure.py b/tests/reconfigure.py new file mode 100644 index 0000000..4bfacc5 --- /dev/null +++ b/tests/reconfigure.py @@ -0,0 +1,48 @@ +# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved. +# +# 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. +# +from common import KollaCliTest + +from kollacli.api.client import ClientApi +from kollacli.common.allinone import AllInOne + +import unittest + +CLIENT = ClientApi() + + +class TestFunctional(KollaCliTest): + + def test_reconfigure(self): + # test will start with no hosts in the inventory + # reconfigure will throw an exception if it fails + # disable all services first as without it empty groups cause errors + allinone = AllInOne() + for service in allinone.services.keys(): + self.run_cli_cmd('property set enable_%s no' % service) + + msg = '' + try: + job = CLIENT.async_reconfigure() + job.wait() + msg = job.get_console_output() + self.assertEqual(job.get_status(), 0, + 'error performing reconfigure: %s' % msg) + except Exception as e: + self.assertEqual(0, 1, + 'unexpected exception in reconfigure %s, %s' + % (e.message, msg)) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/stop.py b/tests/stop.py new file mode 100644 index 0000000..ea110a3 --- /dev/null +++ b/tests/stop.py @@ -0,0 +1,160 @@ +# Copyright(c) 2016, Oracle and/or its affiliates. All Rights Reserved. +# +# 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. +# +from common import KollaCliTest +from common import TestConfig + +from kollacli.api.client import ClientApi + +import unittest + +TEST_GROUP_NAME = 'test_group' +CLIENT = ClientApi() + +NOT_KNOWN = 'Name or service not known' +UNREACHABLE = 'Status: unreachable' + +ENABLED_SERVICES = [ + 'rabbitmq' + ] + +# after deploy +EXPECTED_CONTAINERS_1 = [ + 'rabbitmq' + ] + + +class TestFunctional(KollaCliTest): + + def test_stop(self): + test_config = TestConfig() + test_config.load() + + # add host to inventory + hostnames = test_config.get_hostnames() + if hostnames: + is_physical_host = True + pwd = test_config.get_password(hostnames[0]) + else: + # No physical hosts in config, use a non-existent host. + # This will generate expected exceptions in all host access + # commands. + hostnames = ['test_deploy_host1'] + is_physical_host = False + pwd = 'test_pwd' + + CLIENT.host_add(hostnames) + + try: + setup_info = {} + for hostname in hostnames: + setup_info[hostname] = {'password': pwd} + CLIENT.host_setup(setup_info) + except Exception as e: + self.assertFalse(is_physical_host, 'host setup exception: %s' % e) + self.assertIn(NOT_KNOWN, '%s' % e, + 'Unexpected exception in host setup: %s' % e) + + # add host to a new deploy group + CLIENT.group_add([TEST_GROUP_NAME]) + group = CLIENT.group_get([TEST_GROUP_NAME])[0] + for hostname in hostnames: + group.add_host(hostname) + # due to required host to group association where there are enabled + # services and we have only one host, move the enabled services + # out of control over to the new group, then move them back to + # control once we are done + control = CLIENT.group_get(['control'])[0] + for servicename in control.get_services(): + if servicename in ENABLED_SERVICES: + control.remove_service(servicename) + group.add_service(servicename) + + # stop services, initialize server + self.log.info('Start stop #1') + job = CLIENT.async_host_stop(hostnames) + self._process_job(job, 'stop #1', is_physical_host) + + self.log.info('updating various properties for the test') + + # disable most services so the test is quicker + enable_service_props = {} + for service in CLIENT.service_get_all(): + if service.get_parent(): + # skip subservices + continue + enable = 'no' + if service.name in ENABLED_SERVICES: + enable = 'yes' + enable_service_props['enable_%s' % service.name] = enable + CLIENT.property_set(enable_service_props) + + predeploy_cmds = test_config.get_predeploy_cmds() + for predeploy_cmd in predeploy_cmds: + self.run_cli_cmd('%s' % predeploy_cmd) + + # do a deploy of a limited set of services + self.log.info('Start a deployment') + job = CLIENT.async_deploy() + self._process_job(job, 'deploy', is_physical_host) + + if is_physical_host: + docker_ps = test_config.run_remote_cmd('docker ps', hostname) + docker_ps = docker_ps.replace('\r', '\n') + for service in CLIENT.service_get_all(): + if service.name not in ENABLED_SERVICES: + self.assertNotIn(service.name, docker_ps, + 'disabled service: %s ' % service.name + + 'is running on host: %s ' % hostname + + 'after deploy.') + for servicename in EXPECTED_CONTAINERS_1: + self.assertIn(servicename, docker_ps, + 'enabled service: %s ' % servicename + + 'is not running on host: %s ' % hostname + + 'after deploy.') + + self.log.info('Start stop #2') + job = CLIENT.async_host_stop(hostnames) + self._process_job(job, 'stop #2', is_physical_host) + + if is_physical_host: + docker_ps = test_config.run_remote_cmd('docker ps', hostname) + for service in CLIENT.service_get_all(): + if service.name not in ENABLED_SERVICES: + self.assertNotIn(service.name, docker_ps, + 'disabled service: %s ' % service.name + + 'is running on host: %s ' % hostname + + 'after stop.') + + def _process_job(self, job, descr, is_physical_host, expect_kill=False): + status = job.wait() + err_msg = job.get_error_message() + self.log.info('job is complete. status: %s, err: %s' + % (status, err_msg)) + if expect_kill: + self.assertEqual(2, status, 'Job %s does not have killed status %s' + % (descr, err_msg)) + else: + if is_physical_host: + self.assertEqual(0, status, 'Job %s failed: %s' + % (descr, err_msg)) + else: + self.assertEqual(1, status, 'Job %s ' % descr + + 'succeeded when it should have failed') + self.assertIn(UNREACHABLE, + 'Job %s: No hosts, but got wrong error: %s' + % (descr, err_msg)) + +if __name__ == '__main__': + unittest.main()