From fd6097fcb211550bbbd76605851451eba6ec5d6f Mon Sep 17 00:00:00 2001 From: James Page Date: Tue, 7 Mar 2017 10:18:33 +0000 Subject: [PATCH] Redux amulet tests Refactor to support direct use of BasicDeployment test class in all gate tests. As hacluster now defaults to using unicast transport, the configuration for multicast device address is no longer required and can be dropped, removing the need to specialize tests on a per series basis. Use min-cluster-size in tests to ensure that pxc clusters build out correctly. Refactor specific test cases into BasicDeployment test class so they get executed against all series, including kill mysqld test pause/resume tests Closes-Bug: 1546577 Change-Id: I239946808f68a0225b49c0327da2b4d35715b837 --- charmhelpers/fetch/snap.py | 122 +++++++++++++++++++ charmhelpers/fetch/ubuntu.py | 80 ++++++++----- tests/basic_deployment.py | 129 +++++++++++++++++++-- tests/charmhelpers/contrib/amulet/utils.py | 29 ++--- tests/dev-broken-mysqld-trusty | 38 ------ tests/dev-kill-mysqld-trusty | 38 ------ tests/dev-multiunit-trusty-min | 43 ------- tests/dev-multiunit-trusty-nomin | 41 ------- tests/dev-pause-and-resume-trusty | 37 ------ tests/dev-single-pause-and-resume-trusty | 39 ------- tests/gate-basic-multiunit-trusty | 29 ----- tests/gate-basic-trusty-icehouse | 8 ++ tests/gate-basic-xenial-mitaka | 40 +------ tests/gate-basic-yakkety-newton | 40 +------ tests/gate-basic-zesty-ocata | 40 +------ tests/gate-single-trusty | 17 --- tests/gate-single-xenial | 17 --- tests/gate-single-yakkety | 17 --- 18 files changed, 314 insertions(+), 490 deletions(-) create mode 100644 charmhelpers/fetch/snap.py delete mode 100755 tests/dev-broken-mysqld-trusty delete mode 100755 tests/dev-kill-mysqld-trusty delete mode 100755 tests/dev-multiunit-trusty-min delete mode 100755 tests/dev-multiunit-trusty-nomin delete mode 100755 tests/dev-pause-and-resume-trusty delete mode 100755 tests/dev-single-pause-and-resume-trusty delete mode 100755 tests/gate-basic-multiunit-trusty create mode 100755 tests/gate-basic-trusty-icehouse delete mode 100755 tests/gate-single-trusty delete mode 100755 tests/gate-single-xenial delete mode 100755 tests/gate-single-yakkety diff --git a/charmhelpers/fetch/snap.py b/charmhelpers/fetch/snap.py new file mode 100644 index 0000000..23c707b --- /dev/null +++ b/charmhelpers/fetch/snap.py @@ -0,0 +1,122 @@ +# Copyright 2014-2017 Canonical Limited. +# +# 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. +""" +Charm helpers snap for classic charms. + +If writing reactive charms, use the snap layer: +https://lists.ubuntu.com/archives/snapcraft/2016-September/001114.html +""" +import subprocess +from os import environ +from time import sleep +from charmhelpers.core.hookenv import log + +__author__ = 'Joseph Borg ' + +SNAP_NO_LOCK = 1 # The return code for "couldn't acquire lock" in Snap (hopefully this will be improved). +SNAP_NO_LOCK_RETRY_DELAY = 10 # Wait X seconds between Snap lock checks. +SNAP_NO_LOCK_RETRY_COUNT = 30 # Retry to acquire the lock X times. + + +class CouldNotAcquireLockException(Exception): + pass + + +def _snap_exec(commands): + """ + Execute snap commands. + + :param commands: List commands + :return: Integer exit code + """ + assert type(commands) == list + + retry_count = 0 + return_code = None + + while return_code is None or return_code == SNAP_NO_LOCK: + try: + return_code = subprocess.check_call(['snap'] + commands, env=environ) + except subprocess.CalledProcessError as e: + retry_count += + 1 + if retry_count > SNAP_NO_LOCK_RETRY_COUNT: + raise CouldNotAcquireLockException('Could not aquire lock after %s attempts' % SNAP_NO_LOCK_RETRY_COUNT) + return_code = e.returncode + log('Snap failed to acquire lock, trying again in %s seconds.' % SNAP_NO_LOCK_RETRY_DELAY, level='WARN') + sleep(SNAP_NO_LOCK_RETRY_DELAY) + + return return_code + + +def snap_install(packages, *flags): + """ + Install a snap package. + + :param packages: String or List String package name + :param flags: List String flags to pass to install command + :return: Integer return code from snap + """ + if type(packages) is not list: + packages = [packages] + + flags = list(flags) + + message = 'Installing snap(s) "%s"' % ', '.join(packages) + if flags: + message += ' with option(s) "%s"' % ', '.join(flags) + + log(message, level='INFO') + return _snap_exec(['install'] + flags + packages) + + +def snap_remove(packages, *flags): + """ + Remove a snap package. + + :param packages: String or List String package name + :param flags: List String flags to pass to remove command + :return: Integer return code from snap + """ + if type(packages) is not list: + packages = [packages] + + flags = list(flags) + + message = 'Removing snap(s) "%s"' % ', '.join(packages) + if flags: + message += ' with options "%s"' % ', '.join(flags) + + log(message, level='INFO') + return _snap_exec(['remove'] + flags + packages) + + +def snap_refresh(packages, *flags): + """ + Refresh / Update snap package. + + :param packages: String or List String package name + :param flags: List String flags to pass to refresh command + :return: Integer return code from snap + """ + if type(packages) is not list: + packages = [packages] + + flags = list(flags) + + message = 'Refreshing snap(s) "%s"' % ', '.join(packages) + if flags: + message += ' with options "%s"' % ', '.join(flags) + + log(message, level='INFO') + return _snap_exec(['refresh'] + flags + packages) diff --git a/charmhelpers/fetch/ubuntu.py b/charmhelpers/fetch/ubuntu.py index 39b9b80..82ac80f 100644 --- a/charmhelpers/fetch/ubuntu.py +++ b/charmhelpers/fetch/ubuntu.py @@ -116,8 +116,8 @@ CLOUD_ARCHIVE_POCKETS = { } APT_NO_LOCK = 100 # The return code for "couldn't acquire lock" in APT. -APT_NO_LOCK_RETRY_DELAY = 10 # Wait 10 seconds between apt lock checks. -APT_NO_LOCK_RETRY_COUNT = 30 # Retry to acquire the lock X times. +CMD_RETRY_DELAY = 10 # Wait 10 seconds between command retries. +CMD_RETRY_COUNT = 30 # Retry a failing fatal command X times. def filter_installed_packages(packages): @@ -249,7 +249,8 @@ def add_source(source, key=None): source.startswith('http') or source.startswith('deb ') or source.startswith('cloud-archive:')): - subprocess.check_call(['add-apt-repository', '--yes', source]) + cmd = ['add-apt-repository', '--yes', source] + _run_with_retries(cmd) elif source.startswith('cloud:'): install(filter_installed_packages(['ubuntu-cloud-keyring']), fatal=True) @@ -286,41 +287,60 @@ def add_source(source, key=None): key]) -def _run_apt_command(cmd, fatal=False): - """Run an APT command. - - Checks the output and retries if the fatal flag is set - to True. +def _run_with_retries(cmd, max_retries=CMD_RETRY_COUNT, retry_exitcodes=(1,), + retry_message="", cmd_env=None): + """Run a command and retry until success or max_retries is reached. :param: cmd: str: The apt command to run. + :param: max_retries: int: The number of retries to attempt on a fatal + command. Defaults to CMD_RETRY_COUNT. + :param: retry_exitcodes: tuple: Optional additional exit codes to retry. + Defaults to retry on exit code 1. + :param: retry_message: str: Optional log prefix emitted during retries. + :param: cmd_env: dict: Environment variables to add to the command run. + """ + + env = os.environ.copy() + if cmd_env: + env.update(cmd_env) + + if not retry_message: + retry_message = "Failed executing '{}'".format(" ".join(cmd)) + retry_message += ". Will retry in {} seconds".format(CMD_RETRY_DELAY) + + retry_count = 0 + result = None + + retry_results = (None,) + retry_exitcodes + while result in retry_results: + try: + result = subprocess.check_call(cmd, env=env) + except subprocess.CalledProcessError as e: + retry_count = retry_count + 1 + if retry_count > max_retries: + raise + result = e.returncode + log(retry_message) + time.sleep(CMD_RETRY_DELAY) + + +def _run_apt_command(cmd, fatal=False): + """Run an apt command with optional retries. + :param: fatal: bool: Whether the command's output should be checked and retried. """ - env = os.environ.copy() - - if 'DEBIAN_FRONTEND' not in env: - env['DEBIAN_FRONTEND'] = 'noninteractive' + # Provide DEBIAN_FRONTEND=noninteractive if not present in the environment. + cmd_env = { + 'DEBIAN_FRONTEND': os.environ.get('DEBIAN_FRONTEND', 'noninteractive')} if fatal: - retry_count = 0 - result = None - - # If the command is considered "fatal", we need to retry if the apt - # lock was not acquired. - - while result is None or result == APT_NO_LOCK: - try: - result = subprocess.check_call(cmd, env=env) - except subprocess.CalledProcessError as e: - retry_count = retry_count + 1 - if retry_count > APT_NO_LOCK_RETRY_COUNT: - raise - result = e.returncode - log("Couldn't acquire DPKG lock. Will retry in {} seconds." - "".format(APT_NO_LOCK_RETRY_DELAY)) - time.sleep(APT_NO_LOCK_RETRY_DELAY) - + _run_with_retries( + cmd, cmd_env=cmd_env, retry_exitcodes=(1, APT_NO_LOCK,), + retry_message="Couldn't acquire DPKG lock") else: + env = os.environ.copy() + env.update(cmd_env) subprocess.call(cmd, env=env) diff --git a/tests/basic_deployment.py b/tests/basic_deployment.py index 7acac7c..f59c4fd 100644 --- a/tests/basic_deployment.py +++ b/tests/basic_deployment.py @@ -1,3 +1,5 @@ +# basic deployment test class for percona-xtradb-cluster + import amulet import re import os @@ -8,9 +10,13 @@ import yaml from charmhelpers.contrib.openstack.amulet.deployment import ( OpenStackAmuletDeployment ) +from charmhelpers.contrib.amulet.utils import AmuletUtils class BasicDeployment(OpenStackAmuletDeployment): + + utils = AmuletUtils() + def __init__(self, vip=None, units=1, series="trusty", openstack=None, source=None, stable=False): super(BasicDeployment, self).__init__(series, openstack, source, @@ -33,6 +39,7 @@ class BasicDeployment(OpenStackAmuletDeployment): ("Please set the vip in local.yaml or " "env var AMULET_OS_VIP to run this test " "suite")) + self.log = self.utils.get_logger() def _add_services(self): """Add services @@ -60,20 +67,21 @@ class BasicDeployment(OpenStackAmuletDeployment): """Configure all of the services.""" cfg_percona = {'sst-password': 'ubuntu', 'root-password': 't00r', + 'min-cluster-size': self.units, 'vip': self.vip} cfg_ha = {'debug': True, - 'corosync_mcastaddr': '226.94.1.4', 'corosync_key': ('xZP7GDWV0e8Qs0GxWThXirNNYlScgi3sRTdZk/IXKD' 'qkNFcwdCWfRQnqrHU/6mb6sz6OIoZzX2MtfMQIDcXu' 'PqQyvKuv7YbRyGHmQwAWDUA4ed759VWAO39kHkfWp9' 'y5RRk/wcHakTcWYMwm70upDGJEP00YT3xem3NQy27A' 'C1w=')} - configs = {'percona-cluster': cfg_percona} + configs = {} if self.units > 1: cfg_ha['cluster_count'] = str(self.units) configs['hacluster'] = cfg_ha + configs['percona-cluster'] = cfg_percona return configs @@ -86,7 +94,23 @@ class BasicDeployment(OpenStackAmuletDeployment): self._configure_services() self._deploy() self.d.sentry.wait() + self.test_deployment() + def test_deployment(self): + '''Top level test function executor''' + self.test_pacemaker() + self.test_pxc_running() + self.test_bootstrapped_and_clustered() + self.test_pause_resume() + self.test_kill_master() + + def test_pacemaker(self): + ''' + Ensure that pacemaker and corosync are correctly configured in + clustered deployments. + + side effect: self.master_unit should be set after execution + ''' if self.units > 1: i = 0 while i < 30 and not self.master_unit: @@ -108,9 +132,87 @@ class BasicDeployment(OpenStackAmuletDeployment): else: self.master_unit = self.find_master(ha=False) + def test_pxc_running(self): + ''' + Ensure PXC is running on all units + ''' for unit in self.d.sentry['percona-cluster']: assert self.is_mysqld_running(unit), 'mysql not running: %s' % unit + def test_bootstrapped_and_clustered(self): + ''' + Ensure PXC is bootstrapped and that peer units are clustered + ''' + self.log.info('Ensuring PXC is bootstrapped') + msg = "Percona cluster failed to bootstrap" + assert self.is_pxc_bootstrapped(), msg + + self.log.info('Checking PXC cluster size == {}'.format(self.units)) + got = int(self.get_cluster_size()) + msg = ("Percona cluster unexpected size" + " (wanted=%s, got=%s)" % (self.units, got)) + assert got == self.units, msg + + def test_pause_resume(self): + ''' + Ensure pasue/resume actions stop/start mysqld on units + ''' + self.log.info('Testing pause/resume actions') + self.log.info('Pausing service on first PXC unit') + unit = self.d.sentry['percona-cluster'][0] + assert self.is_mysqld_running(unit), 'mysql not running' + assert self.utils.status_get(unit)[0] == "active" + + action_id = self.utils.run_action(unit, "pause") + assert self.utils.wait_on_action(action_id), "Pause action failed." + + # Note that is_mysqld_running will print an error message when + # mysqld is not running. This is by design but it looks odd + # in the output. + assert not self.is_mysqld_running(unit=unit), \ + "mysqld is still running!" + + self.log.info('Resuming service on first PXC unit') + assert self.utils.status_get(unit)[0] == "maintenance" + action_id = self.utils.run_action(unit, "resume") + assert self.utils.wait_on_action(action_id), "Resume action failed" + assert self.utils.status_get(unit)[0] == "active" + assert self.is_mysqld_running(unit=unit), \ + "mysqld not running after resume." + + def test_kill_master(self): + ''' + Ensure that killing the mysqld on the master unit results + in a VIP failover + ''' + self.log.info('Testing failover of master unit on mysqld failure') + # we are going to kill the master + old_master = self.master_unit + self.log.info( + 'kill -9 mysqld on {}'.format(self.master_unit.info['unit_name']) + ) + self.master_unit.run('sudo killall -9 mysqld') + + self.log.info('looking for the new master') + i = 0 + changed = False + while i < 10 and not changed: + i += 1 + time.sleep(5) # give some time to pacemaker to react + new_master = self.find_master() + + if (new_master and new_master.info['unit_name'] != + old_master.info['unit_name']): + self.log.info( + 'New master unit detected' + ' on {}'.format(new_master.info['unit_name']) + ) + changed = True + + assert changed, "The master didn't change" + + assert self.is_port_open(address=self.vip), 'cannot connect to vip' + def find_master(self, ha=True): for unit in self.d.sentry['percona-cluster']: if not ha: @@ -118,11 +220,13 @@ class BasicDeployment(OpenStackAmuletDeployment): # is the vip running here? output, code = unit.run('sudo ip a | grep "inet %s/"' % self.vip) - print('---') - print(unit) - print(output) + self.log.info("Checking {}".format(unit.info['unit_name'])) + self.log.debug(output) if code == 0: - print('vip(%s) running in %s' % (self.vip, unit)) + self.log.info('vip ({}) running in {}'.format( + self.vip, + unit.info['unit_name']) + ) return unit def get_pcmkr_resources(self, unit=None): @@ -145,7 +249,7 @@ class BasicDeployment(OpenStackAmuletDeployment): _, code = u.run('pidof mysqld') if code != 0: - print("ERROR: command returned non-zero '%s'" % (code)) + self.log.debug("command returned non-zero '%s'" % (code)) return False return True @@ -160,11 +264,11 @@ class BasicDeployment(OpenStackAmuletDeployment): "grep %s" % (attr, attr)) output, code = u.run(cmd) if code != 0: - print("ERROR: command returned non-zero '%s'" % (code)) + self.log.debug("command returned non-zero '%s'" % (code)) return "" value = re.search(r"^.+?\s+(.+)", output).group(1) - print("%s = %s" % (attr, value)) + self.log.info("%s = %s" % (attr, value)) return value def is_pxc_bootstrapped(self, unit=None): @@ -187,8 +291,9 @@ class BasicDeployment(OpenStackAmuletDeployment): return True except socket.error as e: if e.errno == 113: - print("ERROR: could not connect to %s:%s" % (addr, port)) + self.log.error("could not connect to %s:%s" % (addr, port)) if e.errno == 111: - print("ERROR: connection refused connecting to %s:%s" % (addr, - port)) + self.log.error("connection refused connecting" + " to %s:%s" % (addr, + port)) return False diff --git a/tests/charmhelpers/contrib/amulet/utils.py b/tests/charmhelpers/contrib/amulet/utils.py index f9e4c3a..8a6b764 100644 --- a/tests/charmhelpers/contrib/amulet/utils.py +++ b/tests/charmhelpers/contrib/amulet/utils.py @@ -785,37 +785,30 @@ class AmuletUtils(object): generating test messages which need to be unique-ish.""" return '[{}-{}]'.format(uuid.uuid4(), time.time()) -# amulet juju action helpers: + # amulet juju action helpers: def run_action(self, unit_sentry, action, _check_output=subprocess.check_output, params=None): - """Run the named action on a given unit sentry. + """Translate to amulet's built in run_action(). Deprecated. + + Run the named action on a given unit sentry. params a dict of parameters to use - _check_output parameter is used for dependency injection. + _check_output parameter is no longer used @return action_id. """ - unit_id = unit_sentry.info["unit_name"] - command = ["juju", "action", "do", "--format=json", unit_id, action] - if params is not None: - for key, value in params.iteritems(): - command.append("{}={}".format(key, value)) - self.log.info("Running command: %s\n" % " ".join(command)) - output = _check_output(command, universal_newlines=True) - data = json.loads(output) - action_id = data[u'Action queued with id'] - return action_id + self.log.warn('charmhelpers.contrib.amulet.utils.run_action has been ' + 'deprecated for amulet.run_action') + return unit_sentry.run_action(action, action_args=params) def wait_on_action(self, action_id, _check_output=subprocess.check_output): """Wait for a given action, returning if it completed or not. - _check_output parameter is used for dependency injection. + action_id a string action uuid + _check_output parameter is no longer used """ - command = ["juju", "action", "fetch", "--format=json", "--wait=0", - action_id] - output = _check_output(command, universal_newlines=True) - data = json.loads(output) + data = amulet.actions.get_action_output(action_id, full_output=True) return data.get(u"status") == "completed" def status_get(self, unit): diff --git a/tests/dev-broken-mysqld-trusty b/tests/dev-broken-mysqld-trusty deleted file mode 100755 index ba477fc..0000000 --- a/tests/dev-broken-mysqld-trusty +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python -# test percona-cluster (3 nodes) - -import basic_deployment -import time - - -class ThreeNode(basic_deployment.BasicDeployment): - def __init__(self): - super(ThreeNode, self).__init__(units=3) - - def run(self): - super(ThreeNode, self).run() - # we are going to kill the master - old_master = self.master_unit - print('stopping mysql in %s' % str(self.master_unit.info)) - self.master_unit.run('sudo service mysql stop') - - print('looking for the new master') - i = 0 - changed = False - while i < 10 and not changed: - i += 1 - time.sleep(5) # give some time to pacemaker to react - new_master = self.find_master() - - if (new_master and new_master.info['unit_name'] != - old_master.info['unit_name']): - changed = True - - assert changed, "The master didn't change" - - assert self.is_port_open(address=self.vip), 'cannot connect to vip' - - -if __name__ == "__main__": - t = ThreeNode() - t.run() diff --git a/tests/dev-kill-mysqld-trusty b/tests/dev-kill-mysqld-trusty deleted file mode 100755 index 09c6c7d..0000000 --- a/tests/dev-kill-mysqld-trusty +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python -# test percona-cluster (3 nodes) - -import basic_deployment -import time - - -class ThreeNode(basic_deployment.BasicDeployment): - def __init__(self): - super(ThreeNode, self).__init__(units=3) - - def run(self): - super(ThreeNode, self).run() - # we are going to kill the master - old_master = self.master_unit - print('kill-9 mysqld in %s' % str(self.master_unit.info)) - self.master_unit.run('sudo killall -9 mysqld') - - print('looking for the new master') - i = 0 - changed = False - while i < 10 and not changed: - i += 1 - time.sleep(5) # give some time to pacemaker to react - new_master = self.find_master() - - if (new_master and new_master.info['unit_name'] != - old_master.info['unit_name']): - changed = True - - assert changed, "The master didn't change" - - assert self.is_port_open(address=self.vip), 'cannot connect to vip' - - -if __name__ == "__main__": - t = ThreeNode() - t.run() diff --git a/tests/dev-multiunit-trusty-min b/tests/dev-multiunit-trusty-min deleted file mode 100755 index ff691c7..0000000 --- a/tests/dev-multiunit-trusty-min +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python -# test percona-cluster (1 node) -import basic_deployment - - -class MultiNode(basic_deployment.BasicDeployment): - def __init__(self): - super(MultiNode, self).__init__(units=3) - - def _get_configs(self): - """Configure all of the services.""" - cfg_percona = {'sst-password': 'ubuntu', - 'root-password': 't00r', - 'dataset-size': '512M', - 'vip': self.vip, - 'min-cluster-size': 3} - - cfg_ha = {'debug': True, - 'corosync_mcastaddr': '226.94.1.4', - 'corosync_key': ('xZP7GDWV0e8Qs0GxWThXirNNYlScgi3sRTdZk/IXKD' - 'qkNFcwdCWfRQnqrHU/6mb6sz6OIoZzX2MtfMQIDcXu' - 'PqQyvKuv7YbRyGHmQwAWDUA4ed759VWAO39kHkfWp9' - 'y5RRk/wcHakTcWYMwm70upDGJEP00YT3xem3NQy27A' - 'C1w=')} - - configs = {'percona-cluster': cfg_percona} - if self.units > 1: - configs['hacluster'] = cfg_ha - - return configs - - def run(self): - super(MultiNode, self).run() - msg = "Percona cluster failed to bootstrap" - assert self.is_pxc_bootstrapped(), msg - got = self.get_cluster_size() - msg = "Percona cluster unexpected size (wanted=%s, got=%s)" % (3, got) - assert got == '3', msg - - -if __name__ == "__main__": - t = MultiNode() - t.run() diff --git a/tests/dev-multiunit-trusty-nomin b/tests/dev-multiunit-trusty-nomin deleted file mode 100755 index 5a95f9c..0000000 --- a/tests/dev-multiunit-trusty-nomin +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python -# test percona-cluster (multi node) -import basic_deployment - - -class MultiNode(basic_deployment.BasicDeployment): - def __init__(self): - super(MultiNode, self).__init__(units=2) - - def _get_configs(self): - """Configure all of the services.""" - cfg_percona = {'sst-password': 'ubuntu', - 'root-password': 't00r', - 'dataset-size': '512M', - 'vip': self.vip, - 'min-cluster-size': 3} - - cfg_ha = {'debug': True, - 'corosync_mcastaddr': '226.94.1.4', - 'corosync_key': ('xZP7GDWV0e8Qs0GxWThXirNNYlScgi3sRTdZk/IXKD' - 'qkNFcwdCWfRQnqrHU/6mb6sz6OIoZzX2MtfMQIDcXu' - 'PqQyvKuv7YbRyGHmQwAWDUA4ed759VWAO39kHkfWp9' - 'y5RRk/wcHakTcWYMwm70upDGJEP00YT3xem3NQy27A' - 'C1w=')} - - configs = {'percona-cluster': cfg_percona} - if self.units > 1: - configs['hacluster'] = cfg_ha - - return configs - - def run(self): - super(MultiNode, self).run() - got = self.get_cluster_size() - msg = "Percona cluster unexpected size (wanted=%s, got=%s)" % (1, got) - assert got in ('0', '1'), msg - - -if __name__ == "__main__": - t = MultiNode() - t.run() diff --git a/tests/dev-pause-and-resume-trusty b/tests/dev-pause-and-resume-trusty deleted file mode 100755 index 6b7fa60..0000000 --- a/tests/dev-pause-and-resume-trusty +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python -# test percona-cluster pause and resume - -import basic_deployment -from charmhelpers.contrib.amulet.utils import AmuletUtils - -utils = AmuletUtils() - - -class PauseResume(basic_deployment.BasicDeployment): - - def run(self): - super(PauseResume, self).run() - unit = self.d.sentry['percona-cluster'][0] - assert self.is_mysqld_running(unit), 'mysql not running' - assert utils.status_get(unit)[0] == "active" - - action_id = utils.run_action(unit, "pause") - assert utils.wait_on_action(action_id), "Pause action failed." - - # Note that is_mysqld_running will print an error message when - # mysqld is not running. This is by design but it looks odd - # in the output. - assert not self.is_mysqld_running(unit=unit), \ - "mysqld is still running!" - - assert utils.status_get(unit)[0] == "maintenance" - action_id = utils.run_action(unit, "resume") - assert utils.wait_on_action(action_id), "Resume action failed" - assert utils.status_get(unit)[0] == "active" - assert self.is_mysqld_running(unit=unit), \ - "mysqld not running after resume." - - -if __name__ == "__main__": - p = PauseResume() - p.run() diff --git a/tests/dev-single-pause-and-resume-trusty b/tests/dev-single-pause-and-resume-trusty deleted file mode 100755 index 3d70ce6..0000000 --- a/tests/dev-single-pause-and-resume-trusty +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python -# test percona-cluster (1 node) with pause and resume. - -from charmhelpers.contrib.openstack.amulet.utils import ( # noqa - OpenStackAmuletUtils, - DEBUG, - # ERROR - ) - -import basic_deployment - - -u = OpenStackAmuletUtils(DEBUG) - - -class SingleNode(basic_deployment.BasicDeployment): - def __init__(self): - super(SingleNode, self).__init__(units=1) - - def run(self): - super(SingleNode, self).run() - assert self.is_pxc_bootstrapped(), "Cluster not bootstrapped" - sentry_unit = self.d.sentry['percona-cluster'][0] - - assert u.status_get(sentry_unit)[0] == "active" - - action_id = u.run_action(sentry_unit, "pause") - assert u.wait_on_action(action_id), "Pause action failed." - assert u.status_get(sentry_unit)[0] == "maintenance" - - action_id = u.run_action(sentry_unit, "resume") - assert u.wait_on_action(action_id), "Resume action failed." - assert u.status_get(sentry_unit)[0] == "active" - u.log.debug('OK') - - -if __name__ == "__main__": - t = SingleNode() - t.run() diff --git a/tests/gate-basic-multiunit-trusty b/tests/gate-basic-multiunit-trusty deleted file mode 100755 index aa2c581..0000000 --- a/tests/gate-basic-multiunit-trusty +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python -# test percona-cluster (3 nodes) - -import basic_deployment -import time - - -class ThreeNode(basic_deployment.BasicDeployment): - def __init__(self): - super(ThreeNode, self).__init__(units=3) - - def run(self): - super(ThreeNode, self).run() - # we are going to kill the master - old_master = self.master_unit - self.master_unit.run('sudo poweroff') - - time.sleep(10) # give some time to pacemaker to react - new_master = self.find_master() - assert new_master is not None, "master unit not found" - assert (new_master.info['public-address'] != - old_master.info['public-address']) - - assert self.is_port_open(address=self.vip), 'cannot connect to vip' - - -if __name__ == "__main__": - t = ThreeNode() - t.run() diff --git a/tests/gate-basic-trusty-icehouse b/tests/gate-basic-trusty-icehouse new file mode 100755 index 0000000..8aecc5a --- /dev/null +++ b/tests/gate-basic-trusty-icehouse @@ -0,0 +1,8 @@ +#!/usr/bin/env python + +import basic_deployment + + +if __name__ == "__main__": + t = basic_deployment.BasicDeployment(units=3, series='trusty') + t.run() diff --git a/tests/gate-basic-xenial-mitaka b/tests/gate-basic-xenial-mitaka index 07d0764..bec7243 100755 --- a/tests/gate-basic-xenial-mitaka +++ b/tests/gate-basic-xenial-mitaka @@ -1,44 +1,8 @@ #!/usr/bin/env python -# test percona-cluster (multi node) + import basic_deployment -class MultiNode(basic_deployment.BasicDeployment): - def __init__(self): - super(MultiNode, self).__init__(units=3, series='xenial') - - def _get_configs(self): - """Configure all of the services.""" - cfg_percona = {'sst-password': 'ubuntu', - 'root-password': 't00r', - 'dataset-size': '512M', - 'vip': self.vip, - 'min-cluster-size': 3, - 'ha-bindiface': 'ens2'} - - cfg_ha = {'debug': True, - 'corosync_mcastaddr': '226.94.1.4', - 'corosync_key': ('xZP7GDWV0e8Qs0GxWThXirNNYlScgi3sRTdZk/IXKD' - 'qkNFcwdCWfRQnqrHU/6mb6sz6OIoZzX2MtfMQIDcXu' - 'PqQyvKuv7YbRyGHmQwAWDUA4ed759VWAO39kHkfWp9' - 'y5RRk/wcHakTcWYMwm70upDGJEP00YT3xem3NQy27A' - 'C1w=')} - - configs = {'percona-cluster': cfg_percona} - if self.units > 1: - configs['hacluster'] = cfg_ha - - return configs - - def run(self): - super(MultiNode, self).run() - msg = "Percona cluster failed to bootstrap" - assert self.is_pxc_bootstrapped(), msg - got = self.get_cluster_size() - msg = "Percona cluster unexpected size (wanted=%s, got=%s)" % (3, got) - assert got == '3', msg - - if __name__ == "__main__": - t = MultiNode() + t = basic_deployment.BasicDeployment(units=3, series='xenial') t.run() diff --git a/tests/gate-basic-yakkety-newton b/tests/gate-basic-yakkety-newton index 4fa3b43..851b44d 100755 --- a/tests/gate-basic-yakkety-newton +++ b/tests/gate-basic-yakkety-newton @@ -1,44 +1,8 @@ #!/usr/bin/env python -# test percona-cluster (multi node) + import basic_deployment -class MultiNode(basic_deployment.BasicDeployment): - def __init__(self): - super(MultiNode, self).__init__(units=3, series='yakkety') - - def _get_configs(self): - """Configure all of the services.""" - cfg_percona = {'sst-password': 'ubuntu', - 'root-password': 't00r', - 'dataset-size': '512M', - 'vip': self.vip, - 'min-cluster-size': 3, - 'ha-bindiface': 'ens2'} - - cfg_ha = {'debug': True, - 'corosync_mcastaddr': '226.94.1.4', - 'corosync_key': ('xZP7GDWV0e8Qs0GxWThXirNNYlScgi3sRTdZk/IXKD' - 'qkNFcwdCWfRQnqrHU/6mb6sz6OIoZzX2MtfMQIDcXu' - 'PqQyvKuv7YbRyGHmQwAWDUA4ed759VWAO39kHkfWp9' - 'y5RRk/wcHakTcWYMwm70upDGJEP00YT3xem3NQy27A' - 'C1w=')} - - configs = {'percona-cluster': cfg_percona} - if self.units > 1: - configs['hacluster'] = cfg_ha - - return configs - - def run(self): - super(MultiNode, self).run() - msg = "Percona cluster failed to bootstrap" - assert self.is_pxc_bootstrapped(), msg - got = self.get_cluster_size() - msg = "Percona cluster unexpected size (wanted=%s, got=%s)" % (3, got) - assert got == '3', msg - - if __name__ == "__main__": - t = MultiNode() + t = basic_deployment.BasicDeployment(units=3, series='yakkety') t.run() diff --git a/tests/gate-basic-zesty-ocata b/tests/gate-basic-zesty-ocata index 59452ac..a599607 100644 --- a/tests/gate-basic-zesty-ocata +++ b/tests/gate-basic-zesty-ocata @@ -1,44 +1,8 @@ #!/usr/bin/env python -# test percona-cluster (multi node) + import basic_deployment -class MultiNode(basic_deployment.BasicDeployment): - def __init__(self): - super(MultiNode, self).__init__(units=3, series='zesty') - - def _get_configs(self): - """Configure all of the services.""" - cfg_percona = {'sst-password': 'ubuntu', - 'root-password': 't00r', - 'dataset-size': '512M', - 'vip': self.vip, - 'min-cluster-size': 3, - 'ha-bindiface': 'ens2'} - - cfg_ha = {'debug': True, - 'corosync_mcastaddr': '226.94.1.4', - 'corosync_key': ('xZP7GDWV0e8Qs0GxWThXirNNYlScgi3sRTdZk/IXKD' - 'qkNFcwdCWfRQnqrHU/6mb6sz6OIoZzX2MtfMQIDcXu' - 'PqQyvKuv7YbRyGHmQwAWDUA4ed759VWAO39kHkfWp9' - 'y5RRk/wcHakTcWYMwm70upDGJEP00YT3xem3NQy27A' - 'C1w=')} - - configs = {'percona-cluster': cfg_percona} - if self.units > 1: - configs['hacluster'] = cfg_ha - - return configs - - def run(self): - super(MultiNode, self).run() - msg = "Percona cluster failed to bootstrap" - assert self.is_pxc_bootstrapped(), msg - got = self.get_cluster_size() - msg = "Percona cluster unexpected size (wanted=%s, got=%s)" % (3, got) - assert got == '3', msg - - if __name__ == "__main__": - t = MultiNode() + t = basic_deployment.BasicDeployment(units=3, series='zesty') t.run() diff --git a/tests/gate-single-trusty b/tests/gate-single-trusty deleted file mode 100755 index 1e91e55..0000000 --- a/tests/gate-single-trusty +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env python -# test percona-cluster (1 node) -import basic_deployment - - -class SingleNode(basic_deployment.BasicDeployment): - def __init__(self): - super(SingleNode, self).__init__(units=1) - - def run(self): - super(SingleNode, self).run() - assert self.is_pxc_bootstrapped(), "Cluster not bootstrapped" - - -if __name__ == "__main__": - t = SingleNode() - t.run() diff --git a/tests/gate-single-xenial b/tests/gate-single-xenial deleted file mode 100755 index 1a4cf59..0000000 --- a/tests/gate-single-xenial +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env python -# test percona-cluster (1 node) -import basic_deployment - - -class SingleNode(basic_deployment.BasicDeployment): - def __init__(self): - super(SingleNode, self).__init__(units=1, series='xenial') - - def run(self): - super(SingleNode, self).run() - assert self.is_pxc_bootstrapped(), "Cluster not bootstrapped" - - -if __name__ == "__main__": - t = SingleNode() - t.run() diff --git a/tests/gate-single-yakkety b/tests/gate-single-yakkety deleted file mode 100755 index 95261af..0000000 --- a/tests/gate-single-yakkety +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env python -# test percona-cluster (1 node) -import basic_deployment - - -class SingleNode(basic_deployment.BasicDeployment): - def __init__(self): - super(SingleNode, self).__init__(units=1, series='yakkety') - - def run(self): - super(SingleNode, self).run() - assert self.is_pxc_bootstrapped(), "Cluster not bootstrapped" - - -if __name__ == "__main__": - t = SingleNode() - t.run()