Files
charm-percona-cluster/unit_tests/test_percona_utils.py
Hervé Beraud 6ff8acbd6a Use unittest.mock instead of mock
The mock third party library was needed for mock support in py2
runtimes. Since we now only support py36 and later, we can use the
standard lib unittest.mock module instead.

Note that https://github.com/openstack/charms.openstack is used during tests
and he need `mock`, unfortunatelly it doesn't declare `mock` in its
requirements so it retrieve mock from other charm project (cross dependency).
So we depend on charms.openstack first and when
Ib1ed5b598a52375e29e247db9ab4786df5b6d142 will be merged then CI
will pass without errors.

Depends-On: Ib1ed5b598a52375e29e247db9ab4786df5b6d142
Change-Id: Ie96a81d19be4f14efc7067ddb9c47827f5255ccf
2021-12-15 11:11:14 +00:00

1465 lines
61 KiB
Python

import collections
import os
import tempfile
from unittest import mock
from charmhelpers.fetch import SourceConfigError
import percona_utils
from test_utils import CharmTestCase, patch_open, FakeKvStore
os.environ['JUJU_UNIT_NAME'] = 'percona-cluster/2'
class UtilsTests(CharmTestCase):
TO_PATCH = [
'config',
'kv',
'is_leader',
'leader_get',
'log',
'relation_ids',
'related_units',
'relation_get',
'relation_set',
'get_db_helper',
'yaml',
]
def setUp(self):
super(UtilsTests, self).setUp(percona_utils, self.TO_PATCH)
@mock.patch("percona_utils.log")
def test_update_empty_hosts_file(self, mock_log):
_map = {'1.2.3.4': 'my-host'}
with tempfile.NamedTemporaryFile(delete=False) as tmpfile:
percona_utils.HOSTS_FILE = tmpfile.name
percona_utils.HOSTS_FILE = tmpfile.name
percona_utils.update_hosts_file(_map)
with open(tmpfile.name, 'r', encoding="UTF-8") as fd:
lines = fd.readlines()
os.remove(tmpfile.name)
self.assertEqual(len(lines), 1)
self.assertEqual(lines[0],
"{} {}\n".format(list(_map.keys())[0],
list(_map.values())[0]))
@mock.patch("percona_utils.log")
def test_update_hosts_file_w_dup(self, mock_log):
_map = {'1.2.3.4': 'my-host'}
with tempfile.NamedTemporaryFile(delete=False) as tmpfile:
percona_utils.HOSTS_FILE = tmpfile.name
with open(tmpfile.name, 'w', encoding="UTF-8") as fd:
fd.write("{} {}\n".format(list(_map.keys())[0],
list(_map.values())[0]))
percona_utils.update_hosts_file(_map)
with open(tmpfile.name, 'r', encoding="UTF-8") as fd:
lines = fd.readlines()
os.remove(tmpfile.name)
self.assertEqual(len(lines), 1)
self.assertEqual(lines[0],
"{} {}\n".format(list(_map.keys())[0],
list(_map.values())[0]))
@mock.patch("percona_utils.log")
def test_update_hosts_file_entry(self, mock_log):
altmap = {'1.1.1.1': 'alt-host'}
_map = collections.OrderedDict()
_map['1.1.1.1'] = 'hostA'
_map['2.2.2.2'] = 'hostB'
_map['3.3.3.3'] = 'hostC'
_map['4.4.4.4'] = 'hostD'
with tempfile.NamedTemporaryFile(delete=False) as tmpfile:
percona_utils.HOSTS_FILE = tmpfile.name
with open(tmpfile.name, 'w', encoding="UTF-8") as fd:
fd.write("#somedata\n")
fd.write("{} {}\n".format(list(altmap.keys())[0],
list(altmap.values())[0]))
percona_utils.update_hosts_file(_map)
with open(percona_utils.HOSTS_FILE, 'r', encoding="UTF-8") as fd:
lines = fd.readlines()
os.remove(tmpfile.name)
self.assertEqual(len(lines), 5)
self.assertEqual(lines[0], "#somedata\n")
self.assertEqual(lines[1],
"{} {}\n".format(list(_map.keys())[0],
list(_map.values())[0]))
self.assertEqual(lines[4],
"{} {}\n".format(list(_map.keys())[3],
list(_map.values())[3]))
@mock.patch("percona_utils.get_cluster_host_ip")
@mock.patch("percona_utils.log")
@mock.patch("percona_utils.config")
@mock.patch("percona_utils.update_hosts_file")
@mock.patch("percona_utils.relation_get")
@mock.patch("percona_utils.related_units")
@mock.patch("percona_utils.relation_ids")
def test_get_cluster_hosts(self, mock_rel_ids, mock_rel_units,
mock_rel_get,
mock_update_hosts_file, mock_config,
mock_log,
mock_get_cluster_host_ip):
mock_rel_ids.return_value = [1]
mock_rel_units.return_value = [2]
mock_get_cluster_host_ip.return_value = '10.2.0.1'
def _mock_rel_get(*args, **kwargs):
return {'private-address': '10.2.0.2',
'bootstrap-uuid': 'UUID'}
mock_rel_get.side_effect = _mock_rel_get
mock_config.side_effect = lambda k: False
hosts = percona_utils.get_cluster_hosts()
self.assertFalse(mock_update_hosts_file.called)
mock_rel_get.assert_called_with(rid=1, unit=2)
self.assertEqual(hosts, ['10.2.0.2'])
@mock.patch("percona_utils.get_cluster_host_ip")
@mock.patch("percona_utils.update_hosts_file")
def test_get_cluster_hosts_sorted(self, mock_update_hosts_file,
mock_get_cluster_host_ip):
self.relation_ids.return_value = [1]
self.related_units.return_value = [5, 4, 3]
mock_get_cluster_host_ip.return_value = '10.2.0.1'
def _mock_rel_get(*args, **kwargs):
unit_id = kwargs.get('unit')
# Generate list in reverse sort order
return {'private-address': '10.2.0.{}'.format(unit_id - 1),
'bootstrap-uuid': 'UUUID'}
self.relation_get.side_effect = _mock_rel_get
self.config.side_effect = lambda k: False
hosts = percona_utils.get_cluster_hosts()
self.assertFalse(mock_update_hosts_file.called)
# Verify the IPs are sorted
self.assertEqual(hosts, ['10.2.0.2', '10.2.0.3', '10.2.0.4'])
@mock.patch("percona_utils.get_cluster_host_ip")
@mock.patch("percona_utils.update_hosts_file")
def test_get_cluster_hosts_none_bootstrapped(self, mock_update_hosts_file,
mock_get_cluster_host_ip):
self.relation_ids.return_value = [1]
self.related_units.return_value = [4, 3, 2]
mock_get_cluster_host_ip.return_value = '10.2.0.1'
def _mock_rel_get(*args, **kwargs):
unit_id = kwargs.get('unit')
# None set bootstrap-uuid
return {'private-address': '10.2.0.{}'.format(unit_id)}
self.relation_get.side_effect = _mock_rel_get
self.config.side_effect = lambda k: False
hosts = percona_utils.get_cluster_hosts()
self.assertFalse(mock_update_hosts_file.called)
# Verify empty list
self.assertEqual(hosts, [])
@mock.patch("percona_utils.get_cluster_host_ip")
@mock.patch("percona_utils.update_hosts_file")
def test_get_cluster_hosts_one_not_bootstrapped(self,
mock_update_hosts_file,
mock_get_cluster_host_ip):
self.relation_ids.return_value = [1]
self.related_units.return_value = [4, 3, 2]
mock_get_cluster_host_ip.return_value = '10.2.0.1'
def _mock_rel_get(*args, **kwargs):
unit_id = kwargs.get('unit')
if unit_id == 3:
# unit/3 does not set bootstrap-uuid
return {'private-address': '10.2.0.{}'.format(unit_id)}
else:
return {'private-address': '10.2.0.{}'.format(unit_id),
'bootstrap-uuid': 'UUUID'}
self.relation_get.side_effect = _mock_rel_get
self.config.side_effect = lambda k: False
hosts = percona_utils.get_cluster_hosts()
self.assertFalse(mock_update_hosts_file.called)
# Verify unit/3 not in the list
self.assertEqual(hosts, ['10.2.0.2', '10.2.0.4'])
@mock.patch.object(percona_utils, 'socket')
@mock.patch("percona_utils.get_cluster_host_ip")
@mock.patch.object(percona_utils, 'get_ipv6_addr')
@mock.patch.object(percona_utils, 'log')
@mock.patch.object(percona_utils, 'config')
@mock.patch.object(percona_utils, 'update_hosts_file')
@mock.patch.object(percona_utils, 'relation_get')
@mock.patch.object(percona_utils, 'related_units')
@mock.patch.object(percona_utils, 'relation_ids')
def test_get_cluster_hosts_ipv6(self, mock_rel_ids, mock_rel_units,
mock_rel_get,
mock_update_hosts_file, mock_config,
mock_log, mock_get_ipv6_addr,
mock_get_cluster_host_ip,
mock_socket):
ipv6addr = '2001:db8:1:0:f816:3eff:fe79:cd'
mock_get_ipv6_addr.return_value = [ipv6addr]
mock_rel_ids.return_value = [88]
mock_rel_units.return_value = [1, 2]
mock_get_cluster_host_ip.return_value = 'hostA'
mock_socket.gethostname.return_value = 'hostA'
def _mock_rel_get(*args, **kwargs):
host_suffix = 'BC'
id = kwargs.get('unit')
hostname = "host{}".format(host_suffix[id - 1])
return {'private-address': '10.0.0.{}'.format(id + 1),
'hostname': hostname,
'bootstrap-uuid': 'UUID'}
config = {'prefer-ipv6': True}
mock_rel_get.side_effect = _mock_rel_get
mock_config.side_effect = lambda k: config.get(k)
hosts = percona_utils.get_cluster_hosts()
mock_update_hosts_file.assert_called_with({ipv6addr: 'hostA',
'10.0.0.2': 'hostB',
'10.0.0.3': 'hostC'})
mock_rel_get.assert_has_calls([mock.call(rid=88, unit=1),
mock.call(rid=88, unit=2)])
self.assertEqual(hosts, ['hostB', 'hostC'])
@mock.patch.object(percona_utils, 'get_address_in_network')
@mock.patch.object(percona_utils, 'log')
@mock.patch.object(percona_utils, 'config')
@mock.patch.object(percona_utils, 'relation_get')
@mock.patch.object(percona_utils, 'related_units')
@mock.patch.object(percona_utils, 'relation_ids')
def test_get_cluster_hosts_w_cluster_network(self, mock_rel_ids,
mock_rel_units,
mock_rel_get,
mock_config,
mock_log,
mock_get_address_in_network):
mock_rel_ids.return_value = [88]
mock_rel_units.return_value = [1, 2]
mock_get_address_in_network.return_value = '10.100.0.1'
def _mock_rel_get(*args, **kwargs):
host_suffix = 'BC'
unit = kwargs.get('unit')
hostname = "host{}".format(host_suffix[unit - 1])
return {'private-address': '10.0.0.{}'.format(unit + 1),
'cluster-address': '10.100.0.{}'.format(unit + 1),
'hostname': hostname,
'bootstrap-uuid': 'UUID'}
config = {'cluster-network': '10.100.0.0/24'}
mock_rel_get.side_effect = _mock_rel_get
mock_config.side_effect = lambda k: config.get(k)
hosts = percona_utils.get_cluster_hosts()
mock_rel_get.assert_has_calls([mock.call(rid=88, unit=1),
mock.call(rid=88, unit=2)])
self.assertEqual(hosts, ['10.100.0.2', '10.100.0.3'])
@mock.patch.object(percona_utils, 'is_leader')
@mock.patch.object(percona_utils, 'related_units')
@mock.patch.object(percona_utils, 'relation_ids')
@mock.patch.object(percona_utils, 'config')
def test_is_sufficient_peers(self, mock_config, mock_relation_ids,
mock_related_units, mock_is_leader):
mock_is_leader.return_value = False
_config = {'min-cluster-size': None}
mock_config.side_effect = lambda key: _config.get(key)
self.assertTrue(percona_utils.is_sufficient_peers())
mock_is_leader.return_value = False
mock_relation_ids.return_value = ['cluster:0']
mock_related_units.return_value = ['test/0']
_config = {'min-cluster-size': 3}
mock_config.side_effect = lambda key: _config.get(key)
self.assertFalse(percona_utils.is_sufficient_peers())
mock_is_leader.return_value = False
mock_related_units.return_value = ['test/0', 'test/1']
_config = {'min-cluster-size': 3}
mock_config.side_effect = lambda key: _config.get(key)
self.assertTrue(percona_utils.is_sufficient_peers())
@mock.patch.object(percona_utils, 'lsb_release')
def test_packages_eq_wily(self, mock_lsb_release):
mock_lsb_release.return_value = {'DISTRIB_CODENAME': 'wily'}
self.assertEqual(percona_utils.determine_packages(),
['percona-xtradb-cluster-server'])
@mock.patch.object(percona_utils, 'lsb_release')
def test_packages_gt_wily(self, mock_lsb_release):
mock_lsb_release.return_value = {'DISTRIB_CODENAME': 'xenial'}
self.assertEqual(percona_utils.determine_packages(),
['percona-xtradb-cluster-server'])
@mock.patch.object(percona_utils, 'lsb_release')
def test_packages_lt_wily(self, mock_lsb_release):
mock_lsb_release.return_value = {'DISTRIB_CODENAME': 'trusty'}
self.assertEqual(percona_utils.determine_packages(),
['percona-xtradb-cluster-server-5.5',
'percona-xtradb-cluster-client-5.5'])
@mock.patch.object(percona_utils, 'get_wsrep_value')
def test_cluster_in_sync_not_ready(self, _wsrep_value):
_wsrep_value.side_effect = [None, None]
self.assertFalse(percona_utils.cluster_in_sync())
@mock.patch.object(percona_utils, 'get_wsrep_value')
def test_cluster_in_sync_ready_syncing(self, _wsrep_value):
_wsrep_value.side_effect = [True, None]
self.assertFalse(percona_utils.cluster_in_sync())
@mock.patch.object(percona_utils, 'get_wsrep_value')
def test_cluster_in_sync_ready_sync(self, _wsrep_value):
_wsrep_value.side_effect = [True, 4]
self.assertTrue(percona_utils.cluster_in_sync())
@mock.patch.object(percona_utils, 'get_wsrep_value')
def test_cluster_in_sync_ready_sync_donor(self, _wsrep_value):
_wsrep_value.side_effect = [True, 2]
self.assertTrue(percona_utils.cluster_in_sync())
@mock.patch("percona_utils.config")
def test_get_wsrep_provider_options(self, mock_config):
# Empty
_config = {"min-cluster-size": 3}
mock_config.side_effect = lambda key: _config.get(key)
expected = ""
self.assertEqual(percona_utils.get_wsrep_provider_options(),
expected)
# IPv6 only
_config = {"prefer-ipv6": True}
mock_config.side_effect = lambda key: _config.get(key)
expected = "gmcast.listen_addr=tcp://:::4567"
self.assertEqual(percona_utils.get_wsrep_provider_options(),
expected)
# ipv6 and peer_timeout
_config = {"peer-timeout": "PT15S",
"prefer-ipv6": True}
mock_config.side_effect = lambda key: _config.get(key)
expected = ("gmcast.listen_addr=tcp://:::4567;"
"gmcast.peer_timeout=PT15S")
self.assertEqual(percona_utils.get_wsrep_provider_options(),
expected)
# set gcs.fs_limit=10000
_config = {"gcs-fc-limit": 10000}
mock_config.side_effect = lambda key: _config.get(key)
expected = "gcs.fc_limit=10000"
self.assertEqual(percona_utils.get_wsrep_provider_options(),
expected)
# peer_timeout bad setting
_config = {"peer-timeout": "10"}
mock_config.side_effect = lambda key: _config.get(key)
with self.assertRaises(ValueError):
percona_utils.get_wsrep_provider_options()
_config = {"peer-timeout": "PT10M"}
mock_config.side_effect = lambda key: _config.get(key)
with self.assertRaises(ValueError):
percona_utils.get_wsrep_provider_options()
def test_set_ready_on_peers(self):
self.relation_ids.return_value = ["rel:1"]
percona_utils.set_ready_on_peers()
self.relation_set.assert_called_with(relation_id="rel:1", ready=True)
def test_get_min_cluster_size(self):
_config = {}
self.config.side_effect = lambda key: _config.get(key)
self.relation_ids.return_value = ["rel:1"]
self.related_units.return_value = []
self.assertEqual(percona_utils.get_min_cluster_size(), 1)
self.related_units.return_value = ['unit/2', 'unit/9', 'unit/21']
self.assertEqual(percona_utils.get_min_cluster_size(), 4)
_config = {'min-cluster-size': 3}
self.config.side_effect = lambda key: _config.get(key)
self.assertEqual(percona_utils.get_min_cluster_size(), 3)
@mock.patch("percona_utils.time")
@mock.patch("percona_utils.os")
def test_check_for_socket(self, _os, _time):
# Socket exists checking for exists
_os.path.exists.return_value = True
percona_utils.check_for_socket("filename", exists=True)
_time.sleep.assert_not_called()
# Socket does not exist checking for exists
_os.path.exists.return_value = False
with self.assertRaises(Exception):
percona_utils.check_for_socket("filename", exists=True)
_time.sleep.assert_called_with(10)
_time.reset_mock()
# Socket does not exist checking for not exists
_os.path.exists.return_value = False
percona_utils.check_for_socket("filename", exists=False)
_time.sleep.assert_not_called()
# Socket exists checking for not exists
_os.path.exists.return_value = True
with self.assertRaises(Exception):
percona_utils.check_for_socket("filename", exists=False)
_time.sleep.assert_called_with(10)
def test_check_mysql_connection(self):
_db_helper = mock.MagicMock()
_db_helper.get_mysql_root_password.return_value = "password"
self.get_db_helper.return_value = _db_helper
_db_helper.connect.return_value = mock.MagicMock()
self.assertTrue(percona_utils.check_mysql_connection())
# The MySQLdb module is fully mocked out, including the
# OperationalError. Make OperationalError behave like an exception.
percona_utils.OperationalError = Exception
_db_helper.connect.side_effect = percona_utils.OperationalError
self.assertFalse(percona_utils.check_mysql_connection())
@mock.patch("percona_utils.resolve_data_dir")
@mock.patch("percona_utils.os")
def test_get_grastate(self, _os, _resolve_dd):
_bootstrap = "1"
_seqno = "5422"
_data = {"seqno": _seqno, "safe_to_bootstrap": _bootstrap}
_os.path.exists.return_value = True
_resolve_dd.return_value = "/tmp"
self.yaml.safe_load.return_value = _data
with patch_open() as (_open, _file):
_open.return_value = _file
self.assertEqual(
_data, percona_utils.get_grastate())
@mock.patch("percona_utils.get_grastate")
def test_get_grastate_seqno(self, _get_grastate):
_seqno = "25"
_get_grastate.return_value = {"seqno": _seqno}
self.assertEqual(_seqno, percona_utils.get_grastate_seqno())
@mock.patch("percona_utils.get_grastate")
def test_get_grastate_safe_to_bootstrap(self, _get_grastate):
_bootstrap = "0"
_get_grastate.return_value = {"safe_to_bootstrap": _bootstrap}
self.assertEqual(
_bootstrap, percona_utils.get_grastate_safe_to_bootstrap())
@mock.patch("percona_utils.resolve_data_dir")
@mock.patch("percona_utils.os")
def test_set_grastate_safe_to_bootstrap(self, _os, _resolve_dd):
_resolve_dd.return_value = "/tmp"
_bootstrap = "0"
_os.path.exists.return_value = True
self.yaml.safe_load.return_value = {"safe_to_bootstrap": _bootstrap}
with patch_open() as (_open, _file):
_open.return_value = _file
_file.write = mock.MagicMock()
percona_utils.set_grastate_safe_to_bootstrap()
self.yaml.dump.assert_called_once_with({"safe_to_bootstrap": 1})
_file.write.assert_called_once()
@mock.patch("percona_utils.check_mysql_connection")
@mock.patch("percona_utils.get_wsrep_value")
@mock.patch("percona_utils.notify_bootstrapped")
def test_maybe_notify_bootstrapped(
self, _notify_bootstrapped,
_get_wsrep_value, _check_mysql_connection):
kvstore = mock.MagicMock()
kvstore.get.return_value = True
self.kv.return_value = kvstore
_check_mysql_connection.return_value = False
_uuid = "uuid-uuid"
self.leader_get.return_value = _uuid
_get_wsrep_value.return_value = _uuid
# mysql not runnig
percona_utils.maybe_notify_bootstrapped()
_notify_bootstrapped.assert_not_called()
# No clients initialized
_check_mysql_connection.return_value = True
kvstore.get.return_value = False
percona_utils.maybe_notify_bootstrapped()
_notify_bootstrapped.assert_not_called()
# Differing UUID
_check_mysql_connection.return_value = True
kvstore.get.return_value = True
_get_wsrep_value.return_value = "not-the-same-uuid"
percona_utils.maybe_notify_bootstrapped()
_notify_bootstrapped.assert_not_called()
# Differing UUID
_check_mysql_connection.return_value = True
kvstore.get.return_value = True
_get_wsrep_value.return_value = _uuid
percona_utils.maybe_notify_bootstrapped()
_notify_bootstrapped.assert_called_once_with(cluster_uuid=_uuid)
@mock.patch("percona_utils.add_source")
@mock.patch("percona_utils.apt_update")
def test_update_source(self, mock_apt_update, mock_add_source):
"""Ensure that add_source and apt_update has been called"""
percona_utils.update_source("test-source", key=None)
mock_add_source.assert_called_once_with(
source="test-source", key=None, fail_invalid=True)
mock_apt_update.assert_called_once_with()
@mock.patch("percona_utils.apt_update")
def test_update_invalid_source(self, mock_apt_update):
"""Ensure raise error and set blocked status after invalid source"""
with self.assertRaises(SourceConfigError):
percona_utils.update_source("invalid-source", key=None)
mock_apt_update.assert_not_called()
@mock.patch.object(percona_utils, "nagios_password")
@mock.patch.object(percona_utils, "lsb_release")
@mock.patch.object(percona_utils, "get_db_helper")
@mock.patch.object(percona_utils, "write_nagios_my_cnf")
def test_create_nagios_user(self,
mock_create_nagios_mysql_credential,
mock_get_db_helper,
mock_lsb_release,
mock_nagios_password):
mock_lsb_release.return_value = {'DISTRIB_CODENAME': 'bionic'}
my_mock = mock.Mock()
mock_nagios_password.return_value = "1234"
self.is_leader.return_value = True
mock_get_db_helper.return_value = my_mock
mock_cursor = mock.Mock()
my_mock.connection.cursor.return_value = mock_cursor
percona_utils.create_nagios_user()
my_mock.select.assert_called_once_with(
"SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = 'nagios')"
)
my_mock.execute.assert_has_calls([
mock.call(
"CREATE USER 'nagios'@'localhost' IDENTIFIED BY '1234';"),
])
mock_cursor.execute.assert_not_called()
class OperationalError(Exception):
pass
percona_utils.OperationalError = OperationalError
def mysql_create_user(*args, **kwargs):
raise OperationalError()
my_mock.select.return_value = False
my_mock.execute.side_effect = mysql_create_user
with self.assertRaises(OperationalError):
percona_utils.create_nagios_user()
mock_cursor.execute.assert_not_called()
my_mock.select.return_value = True
percona_utils.create_nagios_user()
mock_cursor.execute.assert_has_calls([
mock.call('UPDATE mysql.user SET authentication_string = '
'PASSWORD( %s ) WHERE user = %s;', ('1234', 'nagios')),
mock.call('FLUSH PRIVILEGES;'),
])
my_mock.connection.commit.assert_called_once_with()
def test_get_nrpe_threads_connected_thresholds(self):
"""Test function for getting and verifying threshold values."""
self.config.return_value = "a,1,2"
with self.assertRaises(ValueError) as context:
percona_utils.get_nrpe_threads_connected_thresholds()
self.assertEqual(ValueError("the wrong number of values was set "
"for the nrpe-threads-connected"),
context.exception)
self.config.return_value = "a,1"
with self.assertRaises(ValueError) as context:
percona_utils.get_nrpe_threads_connected_thresholds()
self.assertEqual(
ValueError("invalid literal for int() with base 10: 'a'"),
context.exception)
self.config.return_value = "50,200"
with self.assertRaises(ValueError) as context:
percona_utils.get_nrpe_threads_connected_thresholds()
self.assertEqual(ValueError("the warning threshold must be in the "
"range [0,100) and the critical "
"threshold must be in the range "
"(0,100]"),
context.exception)
self.config.return_value = "90,60"
with self.assertRaises(ValueError) as context:
percona_utils.get_nrpe_threads_connected_thresholds()
self.assertEqual(ValueError("the warning threshold must be less "
"than critical"),
context.exception)
self.config.return_value = "80,90"
thresholds = percona_utils.get_nrpe_threads_connected_thresholds()
self.assertEqual(thresholds, (80, 90))
class UtilsTestsStatus(CharmTestCase):
TO_PATCH = [
'is_sufficient_peers',
'is_bootstrapped',
'config',
'cluster_in_sync',
'is_leader',
'related_units',
'relation_ids',
'relation_get',
'leader_get',
'is_unit_paused_set',
'is_clustered',
'distributed_wait',
'cluster_ready',
'seeded',
'kv',
]
def setUp(self):
super(UtilsTestsStatus, self).setUp(percona_utils, self.TO_PATCH)
self._kvstore = FakeKvStore()
self.kv.return_value = self._kvstore
_m = mock.patch("charmhelpers.core.unitdata.kv")
self.mock_kv = _m.start()
self.addCleanup(_m.stop)
@mock.patch.object(percona_utils, 'seeded')
def test_single_unit(self, mock_seeded):
mock_seeded.return_value = True
self.config.return_value = None
self.is_sufficient_peers.return_value = True
stat, _ = percona_utils.charm_check_func()
assert stat == 'active'
def test_insufficient_peers(self):
self.config.return_value = 3
self.is_sufficient_peers.return_value = False
stat, _ = percona_utils.charm_check_func()
assert stat == 'blocked'
def test_not_bootstrapped(self):
self.config.return_value = 3
self.is_sufficient_peers.return_value = True
self.is_bootstrapped.return_value = False
stat, _ = percona_utils.charm_check_func()
assert stat == 'waiting'
def test_bootstrapped_in_sync(self):
self.config.return_value = 3
self.is_sufficient_peers.return_value = True
self.is_bootstrapped.return_value = True
self.cluster_in_sync.return_value = True
self.seeded.return_value = True
stat, _ = percona_utils.charm_check_func()
assert stat == 'active'
@mock.patch('time.sleep', return_value=None)
def test_bootstrapped_not_in_sync(self, mock_time):
self.config.return_value = 3
self.is_sufficient_peers.return_value = True
self.is_bootstrapped.return_value = True
self.cluster_in_sync.return_value = False
self.seeded.return_value = True
stat, _ = percona_utils.charm_check_func()
assert stat == 'blocked'
@mock.patch('time.sleep', return_value=None)
def test_bootstrapped_not_in_sync_to_synced(self, mock_time):
self.config.return_value = 3
self.is_sufficient_peers.return_value = True
self.is_bootstrapped.return_value = True
self.cluster_in_sync.side_effect = [False, False, True]
self.seeded.return_value = True
stat, _ = percona_utils.charm_check_func()
assert stat == 'active'
class UtilsTestsCTC(CharmTestCase):
TO_PATCH = [
'is_sufficient_peers',
'config',
'cluster_in_sync',
'is_leader',
'related_units',
'relation_ids',
'relation_get',
'leader_get',
'is_unit_paused_set',
'is_clustered',
'distributed_wait',
'clustered_once',
'kv',
]
def setUp(self):
super(UtilsTestsCTC, self).setUp(percona_utils, self.TO_PATCH)
kvstore = mock.MagicMock()
kvstore.get.return_value = False
self.kv.return_value = kvstore
@mock.patch.object(percona_utils, 'pxc_installed')
@mock.patch.object(percona_utils, 'determine_packages')
@mock.patch.object(percona_utils, 'application_version_set')
@mock.patch.object(percona_utils, 'get_upstream_version')
def test_assess_status(self, get_upstream_version,
application_version_set,
determine_packages,
pxc_installed):
get_upstream_version.return_value = '5.6.17'
determine_packages.return_value = ['percona-xtradb-cluster-server-5.6']
pxc_installed.return_value = True
with mock.patch.object(percona_utils, 'assess_status_func') as asf:
callee = mock.Mock()
asf.return_value = callee
percona_utils.assess_status('test-config')
asf.assert_called_once_with('test-config')
callee.assert_called_once_with()
get_upstream_version.assert_called_with(
'percona-xtradb-cluster-server-5.6'
)
application_version_set.assert_called_with('5.6.17')
@mock.patch.object(percona_utils, 'pxc_installed')
@mock.patch.object(percona_utils, 'determine_packages')
@mock.patch.object(percona_utils, 'application_version_set')
@mock.patch.object(percona_utils, 'get_upstream_version')
def test_assess_status_find_pkg(self, get_upstream_version,
application_version_set,
determine_packages,
pxc_installed):
get_upstream_version.side_effect = [None, None, '5.6.17']
determine_packages.return_value = ['percona-xtradb-cluster-server']
pxc_installed.return_value = True
with mock.patch.object(percona_utils, 'assess_status_func') as asf:
callee = mock.Mock()
asf.return_value = callee
percona_utils.assess_status('test-config')
asf.assert_called_once_with('test-config')
callee.assert_called_once_with()
get_upstream_version.assert_called_with(
'percona-xtradb-cluster-server-5.6'
)
application_version_set.assert_called_with('5.6.17')
@mock.patch.object(percona_utils, 'log')
@mock.patch.object(percona_utils, 'pxc_installed')
@mock.patch.object(percona_utils, 'determine_packages')
@mock.patch.object(percona_utils, 'application_version_set')
@mock.patch.object(percona_utils, 'get_upstream_version')
def test_assess_status_find_pkg_fails(self, get_upstream_version,
application_version_set,
determine_packages,
pxc_installed, log):
get_upstream_version.return_value = None
determine_packages.return_value = ['percona-xtradb-cluster-server']
pxc_installed.return_value = True
with mock.patch.object(percona_utils, 'assess_status_func') as asf:
callee = mock.Mock()
asf.return_value = callee
percona_utils.assess_status('test-config')
asf.assert_called_once_with('test-config')
callee.assert_called_once_with()
get_upstream_version.assert_called_with(
'percona-xtradb-cluster-server-5.7'
)
self.assertFalse(application_version_set.called)
self.assertTrue(log.called)
@mock.patch.object(percona_utils, 'services')
@mock.patch.object(percona_utils, 'REQUIRED_INTERFACES')
@mock.patch.object(percona_utils, 'make_assess_status_func')
def test_assess_status_func(self,
make_assess_status_func,
REQUIRED_INTERFACES,
services):
services.return_value = ['mysql']
percona_utils.assess_status_func('test-config')
# ports=None whilst port checks are disabled.
make_assess_status_func.assert_called_once_with(
'test-config', REQUIRED_INTERFACES, charm_func=mock.ANY,
services=['mysql'], ports=None)
services.assert_called_once()
def test_pause_unit_helper(self):
with mock.patch.object(percona_utils, '_pause_resume_helper') as prh:
percona_utils.pause_unit_helper('random-config')
prh.assert_called_once_with(percona_utils.pause_unit,
'random-config')
with mock.patch.object(percona_utils, '_pause_resume_helper') as prh:
percona_utils.resume_unit_helper('random-config')
prh.assert_called_once_with(percona_utils.resume_unit,
'random-config')
@mock.patch.object(percona_utils, 'services')
def test_pause_resume_helper(self, services):
f = mock.Mock()
services.return_value = 's1'
with mock.patch.object(percona_utils, 'assess_status_func') as asf:
asf.return_value = 'assessor'
percona_utils._pause_resume_helper(f, 'some-config')
asf.assert_called_once_with('some-config')
# ports=None whilst port checks are disabled.
f.assert_called_once_with('assessor', services='s1', ports=None)
@mock.patch.object(percona_utils, 'get_min_cluster_size')
@mock.patch.object(percona_utils, 'seeded')
@mock.patch.object(percona_utils, 'is_sufficient_peers')
def test_is_bootstrapped(self, mock_is_sufficient_peers, mock_seeded,
mock_get_min_cluster_size):
kvstore = mock.MagicMock()
kvstore.get.return_value = False
self.kv.return_value = kvstore
mock_get_min_cluster_size.return_value = 1
# Single unit not yet seeded
self.relation_ids.return_value = []
mock_is_sufficient_peers.return_value = True
mock_seeded.return_value = False
self.assertFalse(percona_utils.is_bootstrapped())
kvstore.set.assert_not_called()
# Single unit seeded
self.relation_ids.return_value = []
mock_is_sufficient_peers.return_value = True
mock_seeded.return_value = True
self.assertTrue(percona_utils.is_bootstrapped())
kvstore.set.assert_called_once_with(key='initial-cluster-complete',
value=True)
# Not sufficient number of peers
kvstore.reset_mock()
mock_get_min_cluster_size.return_value = 3
self.relation_ids.return_value = ['cluster:0']
mock_is_sufficient_peers.return_value = False
self.assertFalse(percona_utils.is_bootstrapped())
# Not all cluster ready
mock_is_sufficient_peers.return_value = True
self.relation_ids.return_value = ['cluster:0']
self.related_units.return_value = ['test/0', 'test/1']
self.relation_get.return_value = False
_config = {'min-cluster-size': 3}
self.config.side_effect = lambda key: _config.get(key)
self.assertFalse(percona_utils.is_bootstrapped())
# kvstore.set has not been called with incomplete clusters
kvstore.set.assert_not_called()
# All cluster ready
mock_is_sufficient_peers.return_value = True
self.relation_ids.return_value = ['cluster:0']
self.related_units.return_value = ['test/0', 'test/1']
self.relation_get.return_value = 'UUID'
_config = {'min-cluster-size': 3}
self.config.side_effect = lambda key: _config.get(key)
self.assertTrue(percona_utils.is_bootstrapped())
kvstore.set.assert_called_once_with(key='initial-cluster-complete',
value=True)
# Now set the key for clustered at least once
kvstore.get.return_value = True
kvstore.set.reset_mock()
# Not all cluster ready no min-cluster-size
mock_is_sufficient_peers.return_value = True
self.relation_ids.return_value = ['cluster:0']
self.related_units.return_value = ['test/0', 'test/1']
self.relation_get.return_value = False
_config = {'min-cluster-size': None}
self.config.side_effect = lambda key: _config.get(key)
self.assertFalse(percona_utils.is_bootstrapped())
kvstore.set.assert_not_called()
# All cluster ready no min-cluster-size
mock_is_sufficient_peers.return_value = True
self.relation_ids.return_value = ['cluster:0']
self.related_units.return_value = ['test/0', 'test/1']
self.relation_get.return_value = 'UUID'
_config = {'min-cluster-size': None}
self.config.side_effect = lambda key: _config.get(key)
self.assertTrue(percona_utils.is_bootstrapped())
@mock.patch.object(percona_utils, 'seeded')
def test_cluster_ready(self, mock_seeded):
# Single unit not seeded
_config = {}
mock_seeded.return_value = False
self.config.side_effect = lambda key: _config.get(key)
self.relation_ids.return_value = ['rel:1']
self.related_units.return_value = []
self.assertFalse(percona_utils.cluster_ready())
# Single unit seeded
_config = {}
mock_seeded.return_value = True
self.config.side_effect = lambda key: _config.get(key)
self.relation_ids.return_value = ['rel:1']
self.related_units.return_value = []
self.assertTrue(percona_utils.cluster_ready())
# When VIP configured check is_clustered
_config = {'vip': '10.10.10.10', 'min-cluster-size': 3}
self.config.side_effect = lambda key: _config.get(key)
# HACluster not ready
self.is_clustered.return_value = False
self.assertFalse(percona_utils.cluster_ready())
# HACluster ready peers not ready
self.is_clustered.return_value = True
self.related_units.return_value = ['unit/1', 'unit/2']
self.relation_get.return_value = None
self.assertFalse(percona_utils.cluster_ready())
# HACluster ready one peer ready one not
self.relation_get.side_effect = [True, True, None]
self.assertFalse(percona_utils.cluster_ready())
# HACluster ready one all peers ready
self.relation_get.side_effect = [True, True, True]
self.assertTrue(percona_utils.cluster_ready())
@mock.patch.object(percona_utils, 'cluster_ready')
def test_client_node_is_ready(self, mock_cluster_ready):
# Paused
self.is_unit_paused_set.return_value = True
self.assertFalse(percona_utils.client_node_is_ready())
# Cluster not ready
mock_cluster_ready.return_value = False
self.assertFalse(percona_utils.client_node_is_ready())
# Not ready
self.is_unit_paused_set.return_value = False
mock_cluster_ready.return_value = True
self.relation_ids.return_value = ['shared-db:0']
self.leader_get.return_value = {}
self.assertFalse(percona_utils.client_node_is_ready())
# Ready
self.is_unit_paused_set.return_value = False
mock_cluster_ready.return_value = True
self.relation_ids.return_value = ['shared-db:0']
self.leader_get.return_value = {'shared-db:0_password': 'password'}
self.assertTrue(percona_utils.client_node_is_ready())
@mock.patch.object(percona_utils, 'cluster_ready')
def test_leader_node_is_ready(self, mock_cluster_ready):
# Paused
self.is_unit_paused_set.return_value = True
self.assertFalse(percona_utils.leader_node_is_ready())
# Not leader
self.is_unit_paused_set.return_value = False
self.is_leader.return_value = False
self.assertFalse(percona_utils.leader_node_is_ready())
# Not cluster ready
self.is_unit_paused_set.return_value = False
self.is_leader.return_value = True
mock_cluster_ready.return_value = False
self.assertFalse(percona_utils.leader_node_is_ready())
# Leader ready
self.is_unit_paused_set.return_value = False
self.is_leader.return_value = True
mock_cluster_ready.return_value = True
self.assertTrue(percona_utils.leader_node_is_ready())
def test_cluster_wait(self):
self.relation_ids.return_value = ['amqp:27']
self.related_units.return_value = ['unit/1', 'unit/2', 'unit/3']
# Default check peer relation
_config = {'known-wait': 30}
self.config.side_effect = lambda key: _config.get(key)
percona_utils.cluster_wait()
self.distributed_wait.assert_called_with(modulo=4, wait=30)
# Use Min Cluster Size
_config = {'min-cluster-size': 5, 'known-wait': 30}
self.config.side_effect = lambda key: _config.get(key)
percona_utils.cluster_wait()
self.distributed_wait.assert_called_with(modulo=5, wait=30)
# Override with modulo-nodes
_config = {'min-cluster-size': 5, 'modulo-nodes': 10, 'known-wait': 60}
self.config.side_effect = lambda key: _config.get(key)
percona_utils.cluster_wait()
self.distributed_wait.assert_called_with(modulo=10, wait=60)
# Just modulo-nodes
_config = {'modulo-nodes': 10, 'known-wait': 60}
self.config.side_effect = lambda key: _config.get(key)
percona_utils.cluster_wait()
self.distributed_wait.assert_called_with(modulo=10, wait=60)
class TestResolveHostnameToIP(CharmTestCase):
TO_PATCH = []
def setUp(self):
super(TestResolveHostnameToIP, self).setUp(percona_utils,
self.TO_PATCH)
@mock.patch.object(percona_utils, 'is_ipv6')
@mock.patch.object(percona_utils, 'is_ip')
@mock.patch.object(percona_utils, 'config', lambda *args: None)
def test_resolve_hostname_to_ip_ips(self, mock_is_ip, mock_is_ipv6):
ipv6_address = '2a01:348:2f4:0:dba7:dc58:659b:941f'
ipv4_address = '10.10.10.2'
self.assertEqual(percona_utils.resolve_hostname_to_ip(ipv6_address),
ipv6_address)
self.assertTrue(mock_is_ip.called)
self.assertFalse(mock_is_ipv6.called)
self.assertEqual(percona_utils.resolve_hostname_to_ip(ipv4_address),
ipv4_address)
self.assertTrue(mock_is_ip.called)
self.assertFalse(mock_is_ipv6.called)
@mock.patch.object(percona_utils, 'config', lambda *args: None)
@mock.patch('dns.resolver.query')
def test_resolve_hostname_to_ip_hostname_a(self,
dns_query):
mock_answer = mock.MagicMock()
mock_answer.address = '10.10.10.20'
dns_query.return_value = [mock_answer]
self.assertEqual(percona_utils.resolve_hostname_to_ip('myhostname'),
'10.10.10.20')
dns_query.assert_has_calls([
mock.call('myhostname', 'A'),
])
@mock.patch.object(percona_utils, 'is_ipv6')
@mock.patch.object(percona_utils, 'is_ip')
@mock.patch.object(percona_utils, 'config')
@mock.patch('dns.resolver.query')
def test_resolve_hostname_to_ip_hostname_aaaa(self, dns_query, mock_config,
mock_is_ip, mock_is_ipv6):
def fake_config(key):
return {'prefer-ipv6': True}.get(key)
mock_config.side_effect = fake_config
mock_answer = mock.MagicMock()
mock_is_ipv6.return_value = False
mock_answer.address = '2a01:348:2f4:0:dba7:dc58:659b:941f'
dns_query.return_value = [mock_answer]
self.assertEqual(percona_utils.resolve_hostname_to_ip('myhostname'),
'2a01:348:2f4:0:dba7:dc58:659b:941f')
self.assertFalse(mock_is_ip.called)
self.assertTrue(mock_is_ipv6.called)
dns_query.assert_has_calls([
mock.call('myhostname', 'AAAA'),
])
@mock.patch.object(percona_utils, 'config', lambda *args: None)
@mock.patch('dns.resolver.query')
def test_resolve_hostname_to_ip_hostname_noanswer(self,
dns_query):
dns_query.return_value = []
self.assertEqual(percona_utils.resolve_hostname_to_ip('myhostname'),
None)
dns_query.assert_has_calls([
mock.call('myhostname', 'A'),
])
class TestUpdateBootstrapUUID(CharmTestCase):
TO_PATCH = [
'log',
'leader_get',
'get_wsrep_value',
'relation_ids',
'relation_set',
'is_leader',
'leader_set',
'config',
'leader_get',
]
def setUp(self):
super(TestUpdateBootstrapUUID, self).setUp(percona_utils,
self.TO_PATCH)
self.log.side_effect = self.juju_log
def juju_log(self, msg, level=None):
print("juju-log {}: {}".format(level, msg))
def test_no_bootstrap_uuid(self):
self.leader_get.return_value = None
self.assertRaises(percona_utils.LeaderNoBootstrapUUIDError,
percona_utils.update_bootstrap_uuid)
def test_bootstrap_uuid_already_set(self):
self.leader_get.return_value = '1234-abcd'
def fake_wsrep(k):
d = {'wsrep_ready': 'ON',
'wsrep_cluster_state_uuid': '1234-abcd'}
return d[k]
self.get_wsrep_value.side_effect = fake_wsrep
self.relation_ids.return_value = ['cluster:2']
self.is_leader.return_value = False
percona_utils.update_bootstrap_uuid()
self.relation_set.assert_called_with(relation_id='cluster:2',
**{'bootstrap-uuid': '1234-abcd'})
self.leader_set.assert_not_called()
self.is_leader.return_value = True
percona_utils.update_bootstrap_uuid()
self.relation_set.assert_called_with(relation_id='cluster:2',
**{'bootstrap-uuid': '1234-abcd'})
self.leader_set.assert_called_with(**{'bootstrap-uuid': '1234-abcd'})
@mock.patch.object(percona_utils, 'notify_bootstrapped')
def test_bootstrap_uuid_could_not_be_retrieved(self, mock_notify):
self.leader_get.return_value = '1234-abcd'
def fake_wsrep(k):
d = {'wsrep_ready': 'ON',
'wsrep_cluster_state_uuid': ''}
return d[k]
self.get_wsrep_value.side_effect = fake_wsrep
self.assertFalse(percona_utils.update_bootstrap_uuid())
mock_notify.assert_not_called()
def test_bootstrap_uuid_diffent_uuids(self):
self.leader_get.return_value = '1234-abcd'
def fake_wsrep(k):
d = {'wsrep_ready': 'ON',
'wsrep_cluster_state_uuid': '5678-dead-beef'}
return d[k]
self.get_wsrep_value.side_effect = fake_wsrep
self.assertRaises(percona_utils.InconsistentUUIDError,
percona_utils.update_bootstrap_uuid)
@mock.patch.object(percona_utils, 'check_mysql_connection')
@mock.patch.object(percona_utils, 'leader_set')
@mock.patch.object(percona_utils, 'leader_get')
@mock.patch.object(percona_utils, 'get_db_helper')
def test_update_root_password(self, mock_get_db_helper,
mock_leader_get,
mock_leader_set,
mock_check_mysql_connection):
cur_password = 'openstack'
new_password = 'ubuntu'
leader_config = {
'mysql.passwd': cur_password,
'root-password': cur_password}
_db_helper = mock.Mock()
_db_helper.get_mysql_password.return_value = cur_password
mock_get_db_helper.return_value = _db_helper
mock_leader_get.side_effect = lambda k: leader_config[k]
self.config.side_effect = self.test_config.get
self.assertFalse(percona_utils.update_root_password())
_db_helper.reset_mock()
mock_check_mysql_connection.reset_mock()
self.test_config.set_previous('root-password', cur_password)
self.test_config.set('root-password', new_password)
percona_utils.update_root_password()
_db_helper.connect.assert_called_once_with(
password='openstack',
user='root')
db_exec_calls = [
mock.call("""SET PASSWORD = PASSWORD('ubuntu');"""),
mock.call(
"""SET PASSWORD FOR 'root'@'localhost' """
"""= PASSWORD('ubuntu');""")
]
_db_helper.execute.assert_has_calls(db_exec_calls)
mock_check_mysql_connection.assert_called_once_with(
password='ubuntu')
leader_set_calls = [
mock.call({'root-password': 'ubuntu'}),
mock.call({'mysql.passwd': 'ubuntu'})]
mock_leader_set.assert_has_calls(leader_set_calls)
def test_is_leader_bootstrapped_once(self):
leader_config = {'bootstrap-uuid': None, 'mysql.passwd': None,
'root-password': None, 'sst-password': None}
self.leader_get.return_value = leader_config
self.assertFalse(percona_utils.is_leader_bootstrapped())
leader_config = {'bootstrap-uuid': 'UUID', 'mysql.passwd': None,
'root-password': None, 'sst-password': None}
self.leader_get.return_value = leader_config
self.assertFalse(percona_utils.is_leader_bootstrapped())
leader_config = {'bootstrap-uuid': None, 'mysql.passwd': None,
'root-password': 'pass', 'sst-password': None}
self.leader_get.return_value = leader_config
self.assertFalse(percona_utils.is_leader_bootstrapped())
leader_config = {'bootstrap-uuid': 'UUID', 'mysql.passwd': 'pass',
'root-password': 'pass', 'sst-password': 'pass',
'leader-ip': '10.10.10.10'}
self.leader_get.return_value = leader_config
self.assertTrue(percona_utils.is_leader_bootstrapped())
class TestAsynchronousReplication(CharmTestCase):
TO_PATCH = [
'config',
'leader_get',
'network_get_primary_address',
'related_units',
'relation_get',
'relation_ids',
]
def setUp(self):
super(TestAsynchronousReplication, self).setUp(percona_utils,
self.TO_PATCH)
@mock.patch.object(percona_utils, 'config')
def test_get_databases_to_replicate_no_config_id(self, mock_config):
config = {}
mock_config.side_effect = lambda k: config.get(k)
with self.assertRaises(percona_utils.ClusterIDRequired):
percona_utils.get_databases_to_replicate()
@mock.patch.object(percona_utils, 'config')
def test_get_databases_to_replicate(self, mock_config):
config = {
'cluster-id': 3,
'databases-to-replicate': 'db1:tb1,tb2;db2'}
mock_config.side_effect = lambda k: config.get(k)
percona_utils.get_databases_to_replicate()
self.assertEqual(percona_utils.get_databases_to_replicate(),
([{'database': 'db1', 'tables': ['tb1', 'tb2']},
{'database': 'db2', 'tables': []}]))
@mock.patch.object(percona_utils, 'config')
def test_get_databases_to_replicate_many(self, mock_config):
config = {
'cluster-id': 3,
'databases-to-replicate': 'db1:tb1;db2:tb2;db3;db4;db5:tb5,tb6'}
mock_config.side_effect = lambda k: config.get(k)
percona_utils.get_databases_to_replicate()
self.assertEqual(percona_utils.get_databases_to_replicate(),
([{'database': 'db1', 'tables': ['tb1']},
{'database': 'db2', 'tables': ['tb2']},
{'database': 'db3', 'tables': []},
{'database': 'db4', 'tables': []},
{'database': 'db5', 'tables': ['tb5', 'tb6']}
]))
@mock.patch.object(percona_utils, 'config')
def test_get_databases_to_replicate_space(self, mock_config):
config = {
'cluster-id': 3,
'databases-to-replicate': 'db1 tb1; db2,tb2;db3:db4'}
mock_config.side_effect = lambda k: config.get(k)
with self.assertRaises(percona_utils.InvalidDatabasesToReplicate):
percona_utils.get_databases_to_replicate()
@mock.patch.object(percona_utils, 'config')
def test_get_databases_to_replicate_comma(self, mock_config):
config = {
'cluster-id': 3,
'databases-to-replicate': 'db1:tb1;db2,tb2;db3:db4'}
mock_config.side_effect = lambda k: config.get(k)
with self.assertRaises(percona_utils.InvalidDatabasesToReplicate):
percona_utils.get_databases_to_replicate()
@mock.patch.object(percona_utils, 'create_replication_user')
@mock.patch.object(percona_utils, 'list_replication_users')
def test_configure_master_slave_address_not_in_relation_data(
self, mock_list_replication_users, mock_create_replication_user):
self.relation_ids.return_value = [1]
self.related_units.return_value = [1, 2, 3]
self.relation_get.return_value = None
percona_utils.configure_master()
mock_create_replication_user.assert_not_called()
@mock.patch.object(percona_utils, 'create_replication_user')
@mock.patch.object(percona_utils, 'list_replication_users')
def test_configure_master_slave_address_in_relation_data_and_created(
self, mock_list_replication_users, mock_create_replication_user):
self.relation_ids.return_value = [1]
self.related_units.return_value = [1, 2, 3]
def _mock_rel_get(*args, **kwargs):
unit_id = kwargs.get('unit')
return '10.0.1.{}'.format(unit_id)
self.relation_get.side_effect = _mock_rel_get
mock_list_replication_users.return_value = ['10.0.1.1',
'10.0.1.2',
'10.0.1.3']
percona_utils.configure_master()
mock_create_replication_user.assert_not_called()
@mock.patch.object(percona_utils, 'create_replication_user')
@mock.patch.object(percona_utils, 'list_replication_users')
def test_configure_master_slave_address_in_relation_data_and_not_created(
self, mock_list_replication_users, mock_create_replication_user):
self.relation_ids.return_value = [1]
self.related_units.return_value = [1, 2, 3]
def _mock_rel_get(*args, **kwargs):
unit_id = kwargs.get('unit')
return '10.0.1.{}'.format(unit_id)
self.relation_get.side_effect = _mock_rel_get
mock_list_replication_users.return_value = ['10.0.1.1', '10.0.1.2']
self.leader_get.return_value = 'password'
percona_utils.configure_master()
mock_create_replication_user.assert_called_once_with('10.0.1.3',
'password')
@mock.patch.object(percona_utils, 'get_db_helper')
def test_configure_slave_no_leader(
self, mock_get_db_helper):
my_mock = mock.Mock()
self.relation_ids.return_value = [1]
self.related_units.return_value = [1, 2, 3]
def _mock_rel_get(*args, **kwargs):
return {'private-address': '10.0.0.1'}
self.relation_get.side_effect = _mock_rel_get
mock_get_db_helper.return_value = my_mock
percona_utils.configure_slave()
my_mock.execute.assert_not_called()
@mock.patch.object(percona_utils, 'get_db_helper')
def test_configure_slave_leader_and_no_full_relation_data(
self, mock_get_db_helper):
my_mock = mock.Mock()
self.relation_ids.return_value = [1]
self.related_units.return_value = [1, 2, 3]
def _mock_rel_get(*args, **kwargs):
return {'private-address': '10.0.0.1',
'leader': True}
self.relation_get.side_effect = _mock_rel_get
mock_get_db_helper.return_value = my_mock
percona_utils.configure_slave()
my_mock.execute.assert_not_called()
@mock.patch.object(percona_utils, 'get_db_helper')
def test_configure_slave_leader_and_full_relation_data(
self, mock_get_db_helper):
my_mock = mock.Mock()
self.relation_ids.return_value = [1]
self.related_units.return_value = [1, 2, 3]
def _mock_rel_get(*args, **kwargs):
return {'private-address': '10.0.0.1',
'leader': True,
'master_address': '10.0.0.1',
'master_file': 'file',
'master_password': 'password',
'master_position': 'position'}
self.relation_get.side_effect = _mock_rel_get
mock_get_db_helper.return_value = my_mock
sql1 = "STOP SLAVE;"
sql2 = ("CHANGE MASTER TO "
"master_host='10.0.0.1', "
"master_port=3306, "
"master_user='replication', "
"master_password='password', "
"master_log_file='file', "
"master_log_pos=position;")
sql3 = "START SLAVE;"
percona_utils.configure_slave()
my_mock.execute.assert_any_call(sql1)
my_mock.execute.assert_any_call(sql2)
my_mock.execute.assert_any_call(sql3)
@mock.patch.object(percona_utils, 'get_db_helper')
def test_deconfigure_slave(self, mock_get_db_helper):
my_mock = mock.Mock()
mock_get_db_helper.return_value = my_mock
sql1 = "STOP SLAVE;"
sql2 = "RESET SLAVE ALL;"
percona_utils.deconfigure_slave()
my_mock.execute.assert_any_call(sql1)
my_mock.execute.assert_any_call(sql2)
@mock.patch.object(percona_utils, 'get_db_helper')
def test_get_master_status(self, mock_get_db_helper):
my_mock = mock.Mock()
self.network_get_primary_address.return_value = '10.0.0.1'
mock_get_db_helper.return_value = my_mock
my_mock.select.return_value = [['file', 'position']]
self.assertEqual(percona_utils.get_master_status('master'),
('10.0.0.1', 'file', 'position'))
@mock.patch.object(percona_utils, 'get_db_helper')
def test_get_slave_status(self, mock_get_db_helper):
my_mock = mock.Mock()
mock_get_db_helper.return_value = my_mock
my_mock.select.return_value = [['state', '10.0.0.1']]
self.assertEqual(percona_utils.get_slave_status(), ('10.0.0.1'))
@mock.patch.object(percona_utils, 'get_db_helper')
def test_create_replication_user(self, mock_get_db_helper):
my_mock = mock.Mock()
slave_address = '10.0.1.1'
master_password = 'password'
mock_get_db_helper.return_value = my_mock
sql = ("GRANT REPLICATION SLAVE ON *.* TO 'replication'@'{}' "
"IDENTIFIED BY '{}';").format(slave_address, master_password)
percona_utils.create_replication_user(slave_address, master_password)
my_mock.execute.assert_called_with(sql)
@mock.patch.object(percona_utils, 'get_db_helper')
def test_delete_replication_user(self, mock_get_db_helper):
my_mock = mock.Mock()
slave_address = '10.0.1.1'
mock_get_db_helper.return_value = my_mock
sql = ("DELETE FROM mysql.user WHERE Host='{}' AND "
"User='replication';").format(slave_address)
percona_utils.delete_replication_user(slave_address)
my_mock.execute.assert_called_with(sql)
@mock.patch.object(percona_utils, 'get_db_helper')
def test_list_replication_users(self, mock_get_db_helper):
my_mock = mock.Mock()
mock_get_db_helper.return_value = my_mock
my_mock.select.return_value = [['10.0.0.1'], ['10.0.0.2']]
self.assertEqual(percona_utils.list_replication_users(),
(['10.0.0.1', '10.0.0.2']))