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'])
示例#3
0
    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(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_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()