import os import sys import tempfile import mock sys.modules['MySQLdb'] = mock.Mock() import percona_utils from test_utils import CharmTestCase os.environ['JUJU_UNIT_NAME'] = 'percona-cluster/2' class UtilsTests(CharmTestCase): TO_PATCH = [ 'config', 'log', 'relation_ids', 'related_units', 'relation_get', 'relation_set', ] 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') as fd: lines = fd.readlines() os.remove(tmpfile.name) self.assertEqual(len(lines), 1) self.assertEqual(lines[0], "%s %s\n" % (map.items()[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') as fd: fd.write("%s %s\n" % (map.items()[0])) percona_utils.update_hosts_file(map) with open(tmpfile.name, 'r') as fd: lines = fd.readlines() os.remove(tmpfile.name) self.assertEqual(len(lines), 1) self.assertEqual(lines[0], "%s %s\n" % (map.items()[0])) @mock.patch("percona_utils.log") def test_update_hosts_file_entry(self, mock_log): altmap = {'1.1.1.1': 'alt-host'} map = {'1.1.1.1': 'hostA', '2.2.2.2': 'hostB', '3.3.3.3': 'hostC', '4.4.4.4': 'hostD'} with tempfile.NamedTemporaryFile(delete=False) as tmpfile: percona_utils.HOSTS_FILE = tmpfile.name with open(tmpfile.name, 'w') as fd: fd.write("#somedata\n") fd.write("%s %s\n" % (altmap.items()[0])) percona_utils.update_hosts_file(map) with open(percona_utils.HOSTS_FILE, 'r') as fd: lines = fd.readlines() os.remove(tmpfile.name) self.assertEqual(len(lines), 5) self.assertEqual(lines[0], "#somedata\n") self.assertEqual(lines[1], "%s %s\n" % (map.items()[0])) self.assertEqual(lines[4], "%s %s\n" % (map.items()[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) # 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() 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', ] def setUp(self): super(UtilsTestsStatus, self).setUp(percona_utils, self.TO_PATCH) def test_single_unit(self): 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 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 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] 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) @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, '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, 'is_sufficient_peers') def test_is_bootstrapped(self, mock_is_sufficient_peers): kvstore = mock.MagicMock() kvstore.get.return_value = False self.kv.return_value = kvstore # Not sufficient number of peers 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()) # Assume single unit no-min-cluster-size mock_is_sufficient_peers.return_value = True self.relation_ids.return_value = [] self.related_units.return_value = [] self.relation_get.return_value = None _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, 'is_bootstrapped') def test_cluster_ready(self, mock_is_bootstrapped): # When VIP configured check is_clustered mock_is_bootstrapped.return_value = True _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 self.is_clustered.return_value = 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 %s: %s' % (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('charmhelpers.contrib.database.mysql.leader_set') @mock.patch('charmhelpers.contrib.database.mysql.is_leader') @mock.patch('charmhelpers.contrib.database.mysql.leader_get') def test_update_root_password(self, mock_leader_get, mock_is_leader, mock_leader_set): cur_password = 'openstack' new_password = 'ubuntu' leader_config = {'mysql.passwd': cur_password} mock_leader_get.side_effect = lambda k: leader_config[k] mock_is_leader.return_value = True self.config.side_effect = self.test_config.get self.assertFalse(percona_utils.update_root_password()) self.test_config.set_previous('root-password', cur_password) self.test_config.set('root-password', new_password) percona_utils.update_root_password() mock_leader_set.assert_called_with( settings={'mysql.passwd': new_password}) @mock.patch.object(percona_utils, 'leader_get') @mock.patch.object(percona_utils, 'get_db_helper') def test_update_root_password_None(self, mock_get_db_helper, mock_leader_get): # Test fix for 1744961 my_mock = mock.Mock() mock_get_db_helper.return_value = my_mock self.config.side_effect = self.test_config.get leader_config = {'root-password': 'leaderpass'} mock_leader_get.side_effect = lambda k: leader_config[k] percona_utils.update_root_password() my_mock.set_mysql_root_password.assert_called_once_with('leaderpass') 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())