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'])
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'])
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 } 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, ['hostA', 'hostB', 'hostC'])
def config_changed(): # if we are paused, delay doing any config changed hooks. It is forced on # the resume. if is_unit_paused_set(): return if config('prefer-ipv6'): assert_charm_supports_ipv6() hosts = get_cluster_hosts() clustered = len(hosts) > 1 bootstrapped = is_bootstrapped() # NOTE: only configure the cluster if we have sufficient peers. This only # applies if min-cluster-size is provided and is used to avoid extraneous # configuration changes and premature bootstrapping as the cluster is # deployed. if is_sufficient_peers(): try: # NOTE(jamespage): try with leadership election if is_leader(): log("Leader unit - bootstrap required=%s" % (not bootstrapped), DEBUG) render_config_restart_on_changed(clustered, hosts, bootstrap=not bootstrapped) elif bootstrapped: log("Cluster is bootstrapped - configuring mysql on this node", DEBUG) render_config_restart_on_changed(clustered, hosts) else: log("Not configuring", DEBUG) except NotImplementedError: # NOTE(jamespage): fallback to legacy behaviour. oldest = oldest_peer(peer_units()) if oldest: log("Leader unit - bootstrap required=%s" % (not bootstrapped), DEBUG) render_config_restart_on_changed(clustered, hosts, bootstrap=not bootstrapped) elif bootstrapped: log("Cluster is bootstrapped - configuring mysql on this node", DEBUG) render_config_restart_on_changed(clustered, hosts) else: log("Not configuring", DEBUG) # Notify any changes to the access network update_shared_db_rels() # (re)install pcmkr agent install_mysql_ocf() if relation_ids('ha'): # make sure all the HA resources are (re)created ha_relation_joined() if is_relation_made('nrpe-external-master'): update_nrpe_config()
def test_get_cluster_hosts(self, mock_rel_ids, mock_rel_units, mock_rel_get, mock_get_host_ip, mock_update_hosts_file, mock_config, mock_log): mock_rel_ids.return_value = [1] mock_rel_units.return_value = [2] mock_get_host_ip.return_value = 'hostA' def _mock_rel_get(*args, **kwargs): return {'private-address': '0.0.0.0'} 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, ['hostA', 'hostA'])
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, [])
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'])
def test_get_cluster_hosts_ipv6(self, mock_rel_ids, mock_rel_units, mock_rel_get, mock_get_host_ip, mock_update_hosts_file, mock_config, mock_log, mock_get_ipv6_addr): ipv6addr = '2001:db8:1:0:f816:3eff:fe79:cd' mock_get_ipv6_addr.return_value = [ipv6addr] mock_rel_ids.return_value = [1, 2] mock_rel_units.return_value = [3, 4] mock_get_host_ip.return_value = 'hostA' def _mock_rel_get(*args, **kwargs): return {'private-address': '0.0.0.0', 'hostname': 'hostB'} mock_rel_get.side_effect = _mock_rel_get mock_config.side_effect = lambda k: True hosts = percona_utils.get_cluster_hosts() mock_update_hosts_file.assert_called_with({ipv6addr: 'hostA', '0.0.0.0': 'hostB'}) mock_rel_get.assert_called_with(rid=2, unit=4) self.assertEqual(hosts, ['hostA', 'hostB'])
def test_get_cluster_hosts_ipv6(self, mock_rel_ids, mock_rel_units, mock_rel_get, mock_get_host_ip, mock_update_hosts_file, mock_config, mock_log, mock_get_ipv6_addr): ipv6addr = '2001:db8:1:0:f816:3eff:fe79:cd' mock_get_ipv6_addr.return_value = [ipv6addr] mock_rel_ids.return_value = [1, 2] mock_rel_units.return_value = [3, 4] mock_get_host_ip.return_value = 'hostA' def _mock_rel_get(*args, **kwargs): return {'private-address': '0.0.0.0', 'hostname': 'hostB'} mock_rel_get.side_effect = _mock_rel_get mock_config.side_effect = lambda k: True hosts = percona_utils.get_cluster_hosts() mock_update_hosts_file.assert_called_with({ ipv6addr: 'hostA', '0.0.0.0': 'hostB' }) mock_rel_get.assert_called_with(rid=2, unit=4) self.assertEqual(hosts, ['hostA', 'hostB'])
def config_changed(): # It is critical that the installation is attempted first before any # rendering of the configuration files occurs. # install_percona_xtradb_cluster has the code to decide if this is the # leader or if the leader is bootstrapped and therefore ready for install. install_percona_xtradb_cluster() # if we are paused, delay doing any config changed hooks. It is forced on # the resume. if is_unit_paused_set(): return if config('prefer-ipv6'): assert_charm_supports_ipv6() hosts = get_cluster_hosts() leader_bootstrapped = is_leader_bootstrapped() leader_ip = leader_get('leader-ip') if is_leader(): # If the cluster has not been fully bootstrapped once yet, use an empty # hosts list to avoid restarting the leader node's mysqld during # cluster buildup. # After, the cluster has bootstrapped at least one time, it is much # less likely to have restart collisions. It is then safe to use the # full hosts list and have the leader node's mysqld restart. if not clustered_once(): hosts = [] log("Leader unit - bootstrap required=%s" % (not leader_bootstrapped), DEBUG) render_config_restart_on_changed(hosts, bootstrap=not leader_bootstrapped) elif leader_bootstrapped and is_sufficient_peers(): # Speed up cluster process by bootstrapping when the leader has # bootstrapped if we have expected number of peers if leader_ip not in hosts: # Fix Bug #1738896 hosts = [leader_ip] + hosts log("Leader is bootstrapped - configuring mysql on this node", DEBUG) # Rendering the mysqld.cnf and restarting is bootstrapping for a # non-leader node. render_config_restart_on_changed(hosts) # Assert we are bootstrapped. This will throw an # InconsistentUUIDError exception if UUIDs do not match. update_bootstrap_uuid() else: # Until the bootstrap-uuid attribute is set by the leader, # cluster_ready() will evaluate to False. So it is necessary to # feed this information to the user. status_set('waiting', "Waiting for bootstrap-uuid set by leader") log('Non-leader waiting on leader bootstrap, skipping render', DEBUG) return # Notify any changes to the access network update_client_db_relations() # (re)install pcmkr agent install_mysql_ocf() for rid in relation_ids('ha'): # make sure all the HA resources are (re)created ha_relation_joined(relation_id=rid) if is_relation_made('nrpe-external-master'): update_nrpe_config() open_port(DEFAULT_MYSQL_PORT) # the password needs to be updated only if the node was already # bootstrapped if is_bootstrapped(): update_root_password() set_ready_on_peers()
def config_changed(): # if we are paused or upgrading, delay doing any config changed hooks. # It is forced on the resume. if is_unit_paused_set() or is_unit_upgrading_set(): log("Unit is paused or upgrading. Skipping config_changed", "WARN") return # It is critical that the installation is attempted first before any # rendering of the configuration files occurs. # install_percona_xtradb_cluster has the code to decide if this is the # leader or if the leader is bootstrapped and therefore ready for install. install_percona_xtradb_cluster() if config('prefer-ipv6'): assert_charm_supports_ipv6() hosts = get_cluster_hosts() leader_bootstrapped = is_leader_bootstrapped() leader_ip = leader_get('leader-ip') # Cluster upgrade adds some complication cluster_series_upgrading = leader_get("cluster_series_upgrading") if cluster_series_upgrading: leader = (leader_get('cluster_series_upgrade_leader') == get_relation_ip('cluster')) leader_ip = leader_get('cluster_series_upgrade_leader') else: leader = is_leader() leader_ip = leader_get('leader-ip') if leader: # If the cluster has not been fully bootstrapped once yet, use an empty # hosts list to avoid restarting the leader node's mysqld during # cluster buildup. # After, the cluster has bootstrapped at least one time, it is much # less likely to have restart collisions. It is then safe to use the # full hosts list and have the leader node's mysqld restart. # Empty hosts if cluster_series_upgrading if not clustered_once() or cluster_series_upgrading: hosts = [] log("Leader unit - bootstrap required=%s" % (not leader_bootstrapped), DEBUG) render_config_restart_on_changed(hosts, bootstrap=not leader_bootstrapped) elif (leader_bootstrapped and is_sufficient_peers() and not cluster_series_upgrading): # Skip if cluster_series_upgrading # Speed up cluster process by bootstrapping when the leader has # bootstrapped if we have expected number of peers if leader_ip not in hosts: # Fix Bug #1738896 hosts = [leader_ip] + hosts log("Leader is bootstrapped - configuring mysql on this node", DEBUG) # Rendering the mysqld.cnf and restarting is bootstrapping for a # non-leader node. render_config_restart_on_changed(hosts) # Assert we are bootstrapped. This will throw an # InconsistentUUIDError exception if UUIDs do not match. update_bootstrap_uuid() else: # Until the bootstrap-uuid attribute is set by the leader, # cluster_ready() will evaluate to False. So it is necessary to # feed this information to the user. status_set('waiting', "Waiting for bootstrap-uuid set by leader") log('Non-leader waiting on leader bootstrap, skipping render', DEBUG) return # Notify any changes to the access network update_client_db_relations() # (re)install pcmkr agent install_mysql_ocf() for rid in relation_ids('ha'): # make sure all the HA resources are (re)created ha_relation_joined(relation_id=rid) if is_relation_made('nrpe-external-master'): update_nrpe_config() open_port(DEFAULT_MYSQL_PORT) # the password needs to be updated only if the node was already # bootstrapped if is_bootstrapped(): update_root_password() set_ready_on_peers() # NOTE(tkurek): re-set 'master' relation data if relation_ids('master'): master_joined()