Charmed-Kubernetes/nrpe/mod/charmhelpers/tests/contrib/database/test_mysql.py

950 lines
36 KiB
Python

import os
import mock
import json
import unittest
import sys
import shutil
import tempfile
from collections import OrderedDict
sys.modules['MySQLdb'] = mock.Mock()
from charmhelpers.contrib.database import mysql # noqa
class MysqlTests(unittest.TestCase):
def setUp(self):
super(MysqlTests, self).setUp()
def test_connect_host_defined(self):
helper = mysql.MySQLHelper('foo', 'bar', host='hostA')
with mock.patch.object(mysql, 'log'):
helper.connect(user='user', password='password', host='1.1.1.1')
mysql.MySQLdb.connect.assert_called_with(
passwd='password', host='1.1.1.1', user='user', connect_timeout=30)
def test_connect_host_not_defined(self):
helper = mysql.MySQLHelper('foo', 'bar')
with mock.patch.object(mysql, 'log'):
helper.connect(user='user', password='password')
mysql.MySQLdb.connect.assert_called_with(
passwd='password', host='localhost', user='user',
connect_timeout=30)
def test_connect_port_defined(self):
helper = mysql.MySQLHelper('foo', 'bar')
with mock.patch.object(mysql, 'log'):
helper.connect(user='user', password='password', port=3316)
mysql.MySQLdb.connect.assert_called_with(
passwd='password', host='localhost', user='user', port=3316,
connect_timeout=30)
def test_connect_new_default_timeout(self):
helper = mysql.MySQLHelper('foo', 'bar', connect_timeout=10)
with mock.patch.object(mysql, 'log'):
helper.connect(user='user', password='password', port=3316)
mysql.MySQLdb.connect.assert_called_with(
passwd='password', host='localhost', user='user', port=3316,
connect_timeout=10)
def test_connect_new_default_override(self):
helper = mysql.MySQLHelper('foo', 'bar', connect_timeout=10)
with mock.patch.object(mysql, 'log'):
helper.connect(user='user', password='password', port=3316,
connect_timeout=20)
mysql.MySQLdb.connect.assert_called_with(
passwd='password', host='localhost', user='user', port=3316,
connect_timeout=20)
@mock.patch.object(mysql.MySQLHelper, 'normalize_address')
@mock.patch.object(mysql.MySQLHelper, 'get_mysql_password')
@mock.patch.object(mysql.MySQLHelper, 'grant_exists')
@mock.patch.object(mysql, 'relation_get')
@mock.patch.object(mysql, 'related_units')
@mock.patch.object(mysql, 'log')
def test_get_allowed_units(self, mock_log, mock_related_units,
mock_relation_get,
mock_grant_exists,
mock_get_password,
mock_normalize_address):
# echo
mock_normalize_address.side_effect = lambda addr: addr
def mock_rel_get(unit, rid):
if unit == 'unit/0':
# Non-prefixed settings
d = {'private-address': '10.0.0.1',
'hostname': 'hostA'}
elif unit == 'unit/1':
# Containing prefixed settings
d = {'private-address': '10.0.0.2',
'dbA_hostname': json.dumps(['10.0.0.2', '2001:db8:1::2'])}
elif unit == 'unit/2':
# No hostname
d = {'private-address': '10.0.0.3'}
elif unit == 'unit/3':
# Prefixed hostname
d = {'private-address': '10.0.0.4',
'PRE_hostname': json.dumps(['10.0.0.4', '2001:db8:1::4'])}
return d
mock_relation_get.side_effect = mock_rel_get
mock_related_units.return_value = ['unit/0', 'unit/1', 'unit/2']
helper = mysql.MySQLHelper('foo', 'bar', host='hostA')
units = helper.get_allowed_units('dbA', 'userA')
calls = [mock.call('dbA', 'userA', 'hostA'),
mock.call('dbA', 'userA', '10.0.0.2'),
mock.call('dbA', 'userA', '2001:db8:1::2'),
mock.call('dbA', 'userA', '10.0.0.3')]
helper.grant_exists.assert_has_calls(calls, any_order=True)
self.assertEqual(units, set(['unit/0', 'unit/1', 'unit/2']))
# With prefix
calls = [mock.call('dbB', 'userB', 'hostA'),
mock.call('dbB', 'userB', '10.0.0.2'),
mock.call('dbB', 'userB', '10.0.0.3'),
mock.call('dbB', 'userB', '2001:db8:1::4'),
mock.call('dbB', 'userB', '10.0.0.4')]
mock_related_units.return_value = [
'unit/0', 'unit/1', 'unit/2', 'unit/3']
units = helper.get_allowed_units('dbB', 'userB', prefix="PRE")
helper.grant_exists.assert_has_calls(calls, any_order=True)
self.assertEqual(units, set(['unit/0', 'unit/1', 'unit/2', 'unit/3']))
@mock.patch('charmhelpers.contrib.network.ip.log',
lambda *args, **kwargs: None)
@mock.patch('charmhelpers.contrib.network.ip.ns_query')
@mock.patch('charmhelpers.contrib.network.ip.socket')
@mock.patch.object(mysql, 'unit_get')
@mock.patch.object(mysql, 'config_get')
@mock.patch.object(mysql, 'log')
def test_normalize_address(self, mock_log, mock_config_get, mock_unit_get,
mock_socket, mock_ns_query):
helper = mysql.MySQLHelper('foo', 'bar', host='hostA')
# prefer-ipv6
mock_config_get.return_value = False
# echo
mock_socket.gethostbyname.side_effect = lambda addr: addr
mock_unit_get.return_value = '10.0.0.1'
out = helper.normalize_address('10.0.0.1')
self.assertEqual('127.0.0.1', out)
mock_config_get.assert_called_with('prefer-ipv6')
mock_unit_get.return_value = '10.0.0.1'
out = helper.normalize_address('10.0.0.2')
self.assertEqual('10.0.0.2', out)
mock_config_get.assert_called_with('prefer-ipv6')
out = helper.normalize_address('2001:db8:1::1')
self.assertEqual('2001:db8:1::1', out)
mock_config_get.assert_called_with('prefer-ipv6')
mock_socket.gethostbyname.side_effect = Exception
mock_ns_query.return_value = None
out = helper.normalize_address('unresolvable')
self.assertEqual('unresolvable', out)
mock_config_get.assert_called_with('prefer-ipv6')
# prefer-ipv6
mock_config_get.return_value = True
mock_socket.gethostbyname.side_effect = 'other'
out = helper.normalize_address('unresolvable')
self.assertEqual('unresolvable', out)
mock_config_get.assert_called_with('prefer-ipv6')
def test_passwd_keys(self):
helper = mysql.MySQLHelper('foo', 'bar', host='hostA')
self.assertEqual(list(helper.passwd_keys(None)), ['mysql.passwd'])
self.assertEqual(list(helper.passwd_keys('auser')),
['mysql-auser.passwd', 'auser.passwd'])
@mock.patch.object(mysql.MySQLHelper, 'migrate_passwords_to_leader_storage')
@mock.patch.object(mysql.MySQLHelper, 'get_mysql_password_on_disk')
@mock.patch.object(mysql, 'leader_get')
def test_get_mysql_password_no_peer_passwd(self, mock_leader_get,
mock_get_disk_pw,
mock_migrate_pw):
helper = mysql.MySQLHelper('foo', 'bar', host='hostA')
store = {}
mock_leader_get.side_effect = lambda key: store.get(key)
mock_get_disk_pw.return_value = "disk-passwd"
self.assertEqual(helper.get_mysql_password(), "disk-passwd")
self.assertTrue(mock_migrate_pw.called)
@mock.patch.object(mysql.MySQLHelper, 'migrate_passwords_to_leader_storage')
@mock.patch.object(mysql.MySQLHelper, 'get_mysql_password_on_disk')
@mock.patch.object(mysql, 'leader_get')
def test_get_mysql_password_peer_passwd(self, mock_leader_get,
mock_get_disk_pw, mock_migrate_pw):
helper = mysql.MySQLHelper('foo', 'bar', host='hostA')
store = {'mysql-userA.passwd': 'passwdA'}
mock_leader_get.side_effect = lambda key: store.get(key)
mock_get_disk_pw.return_value = "disk-passwd"
self.assertEqual(helper.get_mysql_password(username='userA'),
"passwdA")
self.assertTrue(mock_migrate_pw.called)
@mock.patch.object(mysql.MySQLHelper, 'migrate_passwords_to_leader_storage')
@mock.patch.object(mysql.MySQLHelper, 'get_mysql_password_on_disk')
@mock.patch.object(mysql, 'leader_get')
def test_get_mysql_password_peer_passwd_legacy(self, mock_leader_get,
mock_get_disk_pw,
mock_migrate_pw):
helper = mysql.MySQLHelper('foo', 'bar', host='hostA')
store = {'userA.passwd': 'passwdA'}
mock_leader_get.side_effect = lambda key: store.get(key)
mock_get_disk_pw.return_value = "disk-passwd"
self.assertEqual(helper.get_mysql_password(username='userA'),
"passwdA")
self.assertTrue(mock_migrate_pw.called)
@mock.patch.object(mysql.MySQLHelper, 'migrate_passwords_to_leader_storage')
@mock.patch.object(mysql.MySQLHelper, 'get_mysql_password_on_disk')
@mock.patch.object(mysql, 'leader_get')
def test_get_mysql_password_peer_passwd_all(self, mock_leader_get,
mock_get_disk_pw,
mock_migrate_pw):
helper = mysql.MySQLHelper('foo', 'bar', host='hostA')
# Add * so we can identify that the new format key takes precedence
# if found.
store = {'mysql-userA.passwd': 'passwdA',
'userA.passwd': 'passwdA*'}
mock_leader_get.side_effect = lambda key: store.get(key)
mock_get_disk_pw.return_value = "disk-passwd"
self.assertEqual(helper.get_mysql_password(username='userA'),
"passwdA")
self.assertTrue(mock_migrate_pw.called)
@mock.patch.object(mysql.MySQLHelper, 'set_mysql_password')
def test_set_mysql_root_password(self, mock_set_passwd):
helper = mysql.MySQLHelper('foo', 'bar', host='hostA')
helper.set_mysql_root_password(password='1234')
mock_set_passwd.assert_called_with(
'root',
'1234',
current_password=None)
@mock.patch.object(mysql.MySQLHelper, 'set_mysql_password')
def test_set_mysql_root_password_cur_passwd(self, mock_set_passwd):
helper = mysql.MySQLHelper('foo', 'bar', host='hostA')
helper.set_mysql_root_password(password='1234', current_password='abc')
mock_set_passwd.assert_called_with(
'root',
'1234',
current_password='abc')
@mock.patch.object(mysql, 'log', lambda *args, **kwargs: None)
@mock.patch.object(mysql, 'is_leader')
@mock.patch.object(mysql, 'leader_get')
@mock.patch.object(mysql, 'leader_set')
@mock.patch.object(mysql, 'CompareHostReleases')
@mock.patch.object(mysql.MySQLHelper, 'get_mysql_password')
@mock.patch.object(mysql.MySQLHelper, 'connect')
def test_set_mysql_password(self, mock_connect, mock_get_passwd,
mock_compare_releases, mock_leader_set,
mock_leader_get, mock_is_leader):
mock_connection = mock.MagicMock()
mock_cursor = mock.MagicMock()
mock_connection.cursor.return_value = mock_cursor
mock_get_passwd.return_value = 'asdf'
mock_is_leader.return_value = True
mock_leader_get.return_value = '1234'
mock_compare_releases.return_value = 'artful'
helper = mysql.MySQLHelper('foo', 'bar', host='hostA')
helper.connection = mock_connection
helper.set_mysql_password(username='root', password='1234')
mock_connect.assert_has_calls(
[mock.call(user='root', password='asdf'), # original password
mock.call(user='root', password='1234')]) # new password
mock_leader_get.assert_has_calls([mock.call('mysql.passwd')])
mock_leader_set.assert_has_calls(
[mock.call(settings={'mysql.passwd': '1234'})]
)
SQL_UPDATE_PASSWD = ("UPDATE mysql.user SET password = "
"PASSWORD( %s ) WHERE user = %s;")
mock_cursor.assert_has_calls(
[mock.call.execute(SQL_UPDATE_PASSWD, ('1234', 'root')),
mock.call.execute('FLUSH PRIVILEGES;'),
mock.call.close(),
mock.call.execute('select 1;'),
mock.call.close()]
)
mock_get_passwd.assert_called_once_with(None)
# make sure for the non-leader leader-set is not called
mock_is_leader.return_value = False
mock_leader_set.reset_mock()
mock_get_passwd.reset_mock()
helper.set_mysql_password(username='root', password='1234')
mock_leader_set.assert_not_called()
mock_get_passwd.assert_called_once_with(None)
mock_get_passwd.reset_mock()
mock_compare_releases.return_value = 'bionic'
helper.set_mysql_password(username='root', password='1234')
SQL_UPDATE_PASSWD = ("UPDATE mysql.user SET "
"authentication_string = "
"PASSWORD( %s ) WHERE user = %s;")
mock_cursor.assert_has_calls(
[mock.call.execute(SQL_UPDATE_PASSWD, ('1234', 'root')),
mock.call.execute('FLUSH PRIVILEGES;'),
mock.call.close(),
mock.call.execute('select 1;'),
mock.call.close()]
)
mock_get_passwd.assert_called_once_with(None)
# Test supplying the current password
mock_is_leader.return_value = False
mock_connect.reset_mock()
mock_get_passwd.reset_mock()
helper.set_mysql_password(
username='root',
password='1234',
current_password='currpass')
self.assertFalse(mock_get_passwd.called)
mock_connect.assert_has_calls(
[mock.call(user='root', password='currpass'), # original password
mock.call(user='root', password='1234')]) # new password
@mock.patch.object(mysql, 'leader_get')
@mock.patch.object(mysql, 'leader_set')
@mock.patch.object(mysql.MySQLHelper, 'get_mysql_password')
@mock.patch.object(mysql.MySQLHelper, 'connect')
def test_set_mysql_password_fail_to_connect(self, mock_connect,
mock_get_passwd,
mock_leader_set,
mock_leader_get):
class FakeOperationalError(Exception):
pass
def fake_connect(*args, **kwargs):
raise FakeOperationalError('foobar')
mysql.MySQLdb.OperationalError = FakeOperationalError
helper = mysql.MySQLHelper('foo', 'bar', host='hostA')
mock_connect.side_effect = fake_connect
self.assertRaises(mysql.MySQLSetPasswordError,
helper.set_mysql_password,
username='root', password='1234')
@mock.patch.object(mysql, 'lsb_release')
@mock.patch.object(mysql, 'leader_get')
@mock.patch.object(mysql, 'leader_set')
@mock.patch.object(mysql.MySQLHelper, 'get_mysql_password')
@mock.patch.object(mysql.MySQLHelper, 'connect')
def test_set_mysql_password_fail_to_connect2(self, mock_connect,
mock_get_passwd,
mock_leader_set,
mock_leader_get,
mock_lsb_release):
class FakeOperationalError(Exception):
def __str__(self):
return 'some-error'
operational_error = FakeOperationalError('foobar')
def fake_connect(user, password):
# fail for the new password
if user == 'root' and password == '1234':
raise operational_error
else:
return mock.MagicMock()
mysql.MySQLdb.OperationalError = FakeOperationalError
helper = mysql.MySQLHelper('foo', 'bar', host='hostA')
helper.connection = mock.MagicMock()
mock_connect.side_effect = fake_connect
mock_lsb_release.return_value = {
'DISTRIB_CODENAME': 'bionic',
}
with self.assertRaises(mysql.MySQLSetPasswordError) as cm:
helper.set_mysql_password(username='root', password='1234')
ex = cm.exception
self.assertEqual(ex.args[0], 'Cannot connect using new password: some-error')
self.assertEqual(ex.args[1], operational_error)
@mock.patch.object(mysql, 'is_leader')
@mock.patch.object(mysql, 'leader_set')
def test_migrate_passwords_to_leader_storage(self, mock_leader_set,
mock_is_leader):
files = {'mysql.passwd': '1',
'userA.passwd': '2',
'mysql-userA.passwd': '3'}
store = {}
def _store(settings):
store.update(settings)
mock_is_leader.return_value = True
tmpdir = tempfile.mkdtemp('charm-helpers-unit-tests')
try:
root_tmplt = "%s/mysql.passwd" % (tmpdir)
helper = mysql.MySQLHelper(root_tmplt, None, host='hostA')
for f in files:
with open(os.path.join(tmpdir, f), 'w') as fd:
fd.write(files[f])
mock_leader_set.side_effect = _store
helper.migrate_passwords_to_leader_storage()
calls = [mock.call(settings={'mysql.passwd': '1'}),
mock.call(settings={'userA.passwd': '2'}),
mock.call(settings={'mysql-userA.passwd': '3'})]
mock_leader_set.assert_has_calls(calls,
any_order=True)
finally:
shutil.rmtree(tmpdir)
# Note that legacy key/val is NOT overwritten
self.assertEqual(store, {'mysql.passwd': '1',
'userA.passwd': '2',
'mysql-userA.passwd': '3'})
@mock.patch.object(mysql, 'log', lambda *args, **kwargs: None)
@mock.patch.object(mysql, 'is_leader')
@mock.patch.object(mysql, 'leader_set')
def test_migrate_passwords_to_leader_storage_not_leader(self, mock_leader_set,
mock_is_leader):
mock_is_leader.return_value = False
tmpdir = tempfile.mkdtemp('charm-helpers-unit-tests')
try:
root_tmplt = "%s/mysql.passwd" % (tmpdir)
helper = mysql.MySQLHelper(root_tmplt, None, host='hostA')
helper.migrate_passwords_to_leader_storage()
finally:
shutil.rmtree(tmpdir)
mock_leader_set.assert_not_called()
class MySQLConfigHelperTests(unittest.TestCase):
def setUp(self):
super(MySQLConfigHelperTests, self).setUp()
self.config_data = {}
self.config = mock.MagicMock()
mysql.config_get = self.config
self.config.side_effect = self._fake_config
def _fake_config(self, key=None):
if key:
try:
return self.config_data[key]
except KeyError:
return None
else:
return self.config_data
@mock.patch.object(mysql.MySQLConfigHelper, 'get_mem_total')
@mock.patch.object(mysql, 'log')
def test_get_innodb_pool_fixed(self, log, mem):
mem.return_value = "100G"
self.config_data = {
'innodb-buffer-pool-size': "50%",
}
helper = mysql.MySQLConfigHelper()
self.assertEqual(
helper.get_innodb_buffer_pool_size(),
helper.human_to_bytes("50G"))
@mock.patch.object(mysql.MySQLConfigHelper, 'get_mem_total')
@mock.patch.object(mysql, 'log')
def test_get_innodb_pool_not_set(self, mog, mem):
mem.return_value = "100G"
self.config_data = {
'innodb-buffer-pool-size': '',
}
helper = mysql.MySQLConfigHelper()
self.assertEqual(
helper.get_innodb_buffer_pool_size(),
helper.DEFAULT_INNODB_BUFFER_SIZE_MAX)
@mock.patch.object(mysql.MySQLConfigHelper, 'get_mem_total')
@mock.patch.object(mysql, 'log')
def test_get_innodb_buffer_unset(self, mog, mem):
mem.return_value = "100G"
self.config_data = {
'innodb-buffer-pool-size': None,
'dataset-size': None,
}
helper = mysql.MySQLConfigHelper()
self.assertEqual(
helper.get_innodb_buffer_pool_size(),
helper.DEFAULT_INNODB_BUFFER_SIZE_MAX)
@mock.patch.object(mysql.MySQLConfigHelper, 'get_mem_total')
@mock.patch.object(mysql, 'log')
def test_get_innodb_buffer_unset_small(self, mog, mem):
mem.return_value = "512M"
self.config_data = {
'innodb-buffer-pool-size': None,
'dataset-size': None,
}
helper = mysql.MySQLConfigHelper()
self.assertEqual(
helper.get_innodb_buffer_pool_size(),
int(helper.human_to_bytes(mem.return_value) *
helper.DEFAULT_INNODB_BUFFER_FACTOR))
@mock.patch.object(mysql.MySQLConfigHelper, 'get_mem_total')
@mock.patch.object(mysql, 'log')
def test_get_innodb_dataset_size(self, mog, mem):
mem.return_value = "100G"
self.config_data = {
'dataset-size': "10G",
}
helper = mysql.MySQLConfigHelper()
self.assertEqual(
helper.get_innodb_buffer_pool_size(),
int(helper.human_to_bytes("10G")))
@mock.patch.object(mysql.MySQLConfigHelper, 'get_mem_total')
@mock.patch.object(mysql, 'log')
def test_get_tuning_level(self, mog, mem):
mem.return_value = "512M"
self.config_data = {
'tuning-level': 'safest',
}
helper = mysql.MySQLConfigHelper()
self.assertEqual(
helper.get_innodb_flush_log_at_trx_commit(),
1
)
@mock.patch.object(mysql.MySQLConfigHelper, 'get_mem_total')
@mock.patch.object(mysql, 'log')
def test_get_tuning_level_fast(self, mog, mem):
mem.return_value = "512M"
self.config_data = {
'tuning-level': 'fast',
}
helper = mysql.MySQLConfigHelper()
self.assertEqual(
helper.get_innodb_flush_log_at_trx_commit(),
2
)
@mock.patch.object(mysql.MySQLConfigHelper, 'get_mem_total')
@mock.patch.object(mysql, 'log')
def test_get_tuning_level_unsafe(self, mog, mem):
mem.return_value = "512M"
self.config_data = {
'tuning-level': 'unsafe',
}
helper = mysql.MySQLConfigHelper()
self.assertEqual(
helper.get_innodb_flush_log_at_trx_commit(),
0
)
@mock.patch.object(mysql.MySQLConfigHelper, 'get_mem_total')
@mock.patch.object(mysql, 'log')
def test_get_innodb_valid_values(self, mog, mem):
mem.return_value = "512M"
self.config_data = {
'innodb-change-buffering': 'all',
}
helper = mysql.MySQLConfigHelper()
self.assertEqual(
helper.get_innodb_change_buffering(),
'all'
)
@mock.patch.object(mysql.MySQLConfigHelper, 'get_mem_total')
@mock.patch.object(mysql, 'log')
def test_get_innodb_invalid_values(self, mog, mem):
mem.return_value = "512M"
self.config_data = {
'innodb-change-buffering': 'invalid',
}
helper = mysql.MySQLConfigHelper()
self.assertTrue(helper.get_innodb_change_buffering() is None)
class PerconaTests(unittest.TestCase):
def setUp(self):
super(PerconaTests, self).setUp()
self.config_data = {}
self.config = mock.MagicMock()
mysql.config_get = self.config
self.config.side_effect = self._fake_config
def _fake_config(self, key=None):
if key:
try:
return self.config_data[key]
except KeyError:
return None
else:
return self.config_data
@mock.patch.object(mysql.MySQLConfigHelper, 'get_mem_total')
@mock.patch.object(mysql, 'log')
def test_parse_config_innodb_pool_fixed(self, log, mem):
mem.return_value = "100G"
self.config_data = {
'innodb-buffer-pool-size': "50%",
}
helper = mysql.PerconaClusterHelper()
mysql_config = helper.parse_config()
self.assertEqual(mysql_config.get('innodb_buffer_pool_size'),
helper.human_to_bytes("50G"))
@mock.patch.object(mysql.MySQLConfigHelper, 'get_mem_total')
@mock.patch.object(mysql, 'log')
def test_parse_config_innodb_pool_not_set(self, mog, mem):
mem.return_value = "100G"
self.config_data = {
'innodb-buffer-pool-size': '',
}
helper = mysql.PerconaClusterHelper()
mysql_config = helper.parse_config()
self.assertEqual(
mysql_config.get('innodb_buffer_pool_size'),
helper.DEFAULT_INNODB_BUFFER_SIZE_MAX)
@mock.patch.object(mysql.MySQLConfigHelper, 'get_mem_total')
@mock.patch.object(mysql, 'log')
def test_parse_config_innodb_buffer_unset(self, mog, mem):
mem.return_value = "100G"
self.config_data = {
'innodb-buffer-pool-size': None,
'dataset-size': None,
}
helper = mysql.PerconaClusterHelper()
mysql_config = helper.parse_config()
self.assertEqual(
mysql_config.get('innodb_buffer_pool_size'),
helper.DEFAULT_INNODB_BUFFER_SIZE_MAX)
@mock.patch.object(mysql.MySQLConfigHelper, 'get_mem_total')
@mock.patch.object(mysql, 'log')
def test_parse_config_innodb_buffer_unset_small(self, mog, mem):
mem.return_value = "512M"
self.config_data = {
'innodb-buffer-pool-size': None,
'dataset-size': None,
}
helper = mysql.PerconaClusterHelper()
mysql_config = helper.parse_config()
self.assertEqual(
mysql_config.get('innodb_buffer_pool_size'),
int(helper.human_to_bytes(mem.return_value) *
helper.DEFAULT_INNODB_BUFFER_FACTOR))
@mock.patch.object(mysql.MySQLConfigHelper, 'get_mem_total')
@mock.patch.object(mysql, 'log')
def test_parse_config_innodb_dataset_size(self, mog, mem):
mem.return_value = "100G"
self.config_data = {
'dataset-size': "10G",
}
helper = mysql.PerconaClusterHelper()
mysql_config = helper.parse_config()
self.assertEqual(
mysql_config.get('innodb_buffer_pool_size'),
int(helper.human_to_bytes("10G")))
@mock.patch.object(mysql.MySQLConfigHelper, 'get_mem_total')
@mock.patch.object(mysql, 'log')
def test_parse_config_wait_timeout(self, mog, mem):
mem.return_value = "100G"
timeout = 314
self.config_data = {
'wait-timeout': timeout,
}
helper = mysql.PerconaClusterHelper()
mysql_config = helper.parse_config()
self.assertEqual(
mysql_config.get('wait_timeout'),
timeout)
@mock.patch.object(mysql.MySQLConfigHelper, 'get_mem_total')
@mock.patch.object(mysql, 'log')
def test_parse_config_tuning_level(self, mog, mem):
mem.return_value = "512M"
self.config_data = {
'tuning-level': 'safest',
}
helper = mysql.PerconaClusterHelper()
mysql_config = helper.parse_config()
self.assertEqual(
mysql_config.get('innodb_flush_log_at_trx_commit'),
1
)
@mock.patch.object(mysql.MySQLConfigHelper, 'get_mem_total')
@mock.patch.object(mysql, 'log')
def test_parse_config_tuning_level_fast(self, mog, mem):
mem.return_value = "512M"
self.config_data = {
'tuning-level': 'fast',
}
helper = mysql.PerconaClusterHelper()
mysql_config = helper.parse_config()
self.assertEqual(
mysql_config.get('innodb_flush_log_at_trx_commit'),
2
)
@mock.patch.object(mysql.MySQLConfigHelper, 'get_mem_total')
@mock.patch.object(mysql, 'log')
def test_parse_config_tuning_level_unsafe(self, mog, mem):
mem.return_value = "512M"
self.config_data = {
'tuning-level': 'unsafe',
}
helper = mysql.PerconaClusterHelper()
mysql_config = helper.parse_config()
self.assertEqual(
mysql_config.get('innodb_flush_log_at_trx_commit'),
0
)
@mock.patch.object(mysql.MySQLConfigHelper, 'get_mem_total')
@mock.patch.object(mysql, 'log')
def test_parse_config_innodb_valid_values(self, mog, mem):
mem.return_value = "512M"
self.config_data = {
'innodb-change-buffering': 'all',
'innodb-io-capacity': 100,
}
helper = mysql.PerconaClusterHelper()
mysql_config = helper.parse_config()
self.assertEqual(
mysql_config.get('innodb_change_buffering'),
'all'
)
self.assertEqual(
mysql_config.get('innodb_io_capacity'),
100
)
@mock.patch.object(mysql.MySQLConfigHelper, 'get_mem_total')
@mock.patch.object(mysql, 'log')
def test_parse_config_innodb_invalid_values(self, mog, mem):
mem.return_value = "512M"
self.config_data = {
'innodb-change-buffering': 'invalid',
}
helper = mysql.PerconaClusterHelper()
mysql_config = helper.parse_config()
self.assertTrue('innodb_change_buffering' not in mysql_config)
self.assertTrue('innodb_io_capacity' not in mysql_config)
class Mysql8Tests(unittest.TestCase):
def setUp(self):
super(Mysql8Tests, self).setUp()
self.template = "/tmp/mysql-passwd.txt"
self.connection = mock.MagicMock()
self.cursor = mock.MagicMock()
self.connection.cursor.return_value = self.cursor
self.helper = mysql.MySQL8Helper(
rpasswdf_template=self.template,
upasswdf_template=self.template)
self.helper.connection = self.connection
self.user = "user"
self.host = "10.5.0.21"
self.password = "passwd"
self.db = "mydb"
def test_grant_exists(self):
# With backticks
self.cursor.fetchall.return_value = (
("GRANT USAGE ON *.* TO `{}`@`{}`".format(self.user, self.host),),
("GRANT ALL PRIVILEGES ON `{}`.* TO `{}`@`{}`"
.format(self.db, self.user, self.host),))
self.assertTrue(self.helper.grant_exists(self.db, self.user, self.host))
self.cursor.execute.assert_called_with(
"SHOW GRANTS FOR '{}'@'{}'".format(self.user, self.host))
# With single quotes
self.cursor.fetchall.return_value = (
("GRANT USAGE ON *.* TO '{}'@'{}'".format(self.user, self.host),),
("GRANT ALL PRIVILEGES ON '{}'.* TO '{}'@'{}'"
.format(self.db, self.user, self.host),))
self.assertTrue(self.helper.grant_exists(self.db, self.user, self.host))
# Grant not there
self.cursor.fetchall.return_value = (
("GRANT USAGE ON *.* TO '{}'@'{}'".format("someuser", "notmyhost"),),
("GRANT ALL PRIVILEGES ON '{}'.* TO '{}'@'{}'"
.format("somedb", "someuser", "notmyhost"),))
self.assertFalse(self.helper.grant_exists(self.db, self.user, self.host))
def test_create_grant(self):
self.helper.grant_exists = mock.MagicMock(return_value=False)
self.helper.create_user = mock.MagicMock()
self.helper.create_grant(self.db, self.user, self.host, self.password)
self.cursor.execute.assert_called_with(
"GRANT ALL PRIVILEGES ON `{}`.* TO '{}'@'{}'"
.format(self.db, self.user, self.host))
self.helper.create_user.assert_called_with(self.user, self.host, self.password)
def test_create_user(self):
self.helper.create_user(self.user, self.host, self.password)
self.cursor.execute.assert_called_with(
"CREATE USER '{}'@'{}' IDENTIFIED BY '{}'".
format(self.user, self.host, self.password))
def test_create_router_grant(self):
self.helper.create_user = mock.MagicMock()
self.helper.create_router_grant(self.user, self.host, self.password)
_calls = [
mock.call("GRANT CREATE USER ON *.* TO '{}'@'{}' WITH GRANT OPTION"
.format(self.user, self.host)),
mock.call("GRANT SELECT, INSERT, UPDATE, DELETE, EXECUTE ON "
"mysql_innodb_cluster_metadata.* TO '{}'@'{}'"
.format(self.user, self.host)),
mock.call("GRANT SELECT ON mysql.user TO '{}'@'{}'"
.format(self.user, self.host)),
mock.call("GRANT SELECT ON "
"performance_schema.replication_group_members TO "
"'{}'@'{}'".format(self.user, self.host)),
mock.call("GRANT SELECT ON "
"performance_schema.replication_group_member_stats TO "
"'{}'@'{}'".format(self.user, self.host)),
mock.call("GRANT SELECT ON "
"performance_schema.global_variables TO "
"'{}'@'{}'".format(self.user, self.host))]
self.cursor.execute.assert_has_calls(_calls)
self.helper.create_user.assert_called_with(self.user, self.host, self.password)
def test_configure_router(self):
self.helper.create_user = mock.MagicMock()
self.helper.create_router_grant = mock.MagicMock()
self.helper.normalize_address = mock.MagicMock(return_value=self.host)
self.helper.get_mysql_password = mock.MagicMock(return_value=self.password)
self.assertEqual(self.password, self.helper.configure_router(self.host, self.user))
self.helper.create_user.assert_called_with(self.user, self.host, self.password)
self.helper.create_router_grant.assert_called_with(self.user, self.host, self.password)
class MysqlHelperTests(unittest.TestCase):
def setUp(self):
super(MysqlHelperTests, self).setUp()
def test_get_prefix(self):
_tests = {
"prefix1": "prefix1_username",
"prefix2": "prefix2_database",
"prefix3": "prefix3_hostname"}
for key in _tests.keys():
self.assertEqual(
key,
mysql.get_prefix(_tests[key]))
def test_get_db_data(self):
_unprefixed = "myprefix"
# Test relation data has every variation of shared-db/db-router data
_relation_data = {
"egress-subnets": "10.5.0.43/32",
"ingress-address": "10.5.0.43",
"nova_database": "nova",
"nova_hostname": "10.5.0.43",
"nova_username": "nova",
"novaapi_database": "nova_api",
"novaapi_hostname": "10.5.0.43",
"novaapi_username": "nova",
"novacell0_database": "nova_cell0",
"novacell0_hostname": "10.5.0.43",
"novacell0_username": "nova",
"private-address": "10.5.0.43",
"database": "keystone",
"username": "keystone",
"hostname": "10.5.0.43",
"mysqlrouter_username":
"mysqlrouteruser",
"mysqlrouter_hostname": "10.5.0.43"}
_expected_data = OrderedDict([
('nova', OrderedDict([('database', 'nova'),
('hostname', '10.5.0.43'),
('username', 'nova')])),
('novaapi', OrderedDict([('database', 'nova_api'),
('hostname', '10.5.0.43'),
('username', 'nova')])),
('novacell0', OrderedDict([('database', 'nova_cell0'),
('hostname', '10.5.0.43'),
('username', 'nova')])),
('mysqlrouter', OrderedDict([('username', 'mysqlrouteruser'),
('hostname', '10.5.0.43')])),
('myprefix', OrderedDict([('hostname', '10.5.0.43'),
('database', 'keystone'),
('username', 'keystone')]))])
_results = mysql.get_db_data(_relation_data, unprefixed=_unprefixed)
for prefix in _expected_data.keys():
for key in _expected_data[prefix].keys():
self.assertEqual(
_results[prefix][key], _expected_data[prefix][key])