def render_config(clustered=False, hosts=[]):
    if not os.path.exists(os.path.dirname(resolve_cnf_file())):
        os.makedirs(os.path.dirname(resolve_cnf_file()))

    context = {
        'cluster_name': 'juju_cluster',
        'private_address': get_host_ip(),
        'clustered': clustered,
        'cluster_hosts': ",".join(hosts),
        'sst_method': config('sst-method'),
        'sst_password': config('sst-password'),
        'innodb_file_per_table': config('innodb-file-per-table'),
        'table_open_cache': config('table-open-cache'),
        'lp1366997_workaround': config('lp1366997-workaround'),
    }

    if config('prefer-ipv6'):
        # NOTE(hopem): this is a kludge to get percona working with ipv6.
        # See lp 1380747 for more info. This is intended as a stop gap until
        # percona package is fixed to support ipv6.
        context['bind_address'] = '::'
        context['wsrep_provider_options'] = 'gmcast.listen_addr=tcp://:::4567;'
        context['ipv6'] = True
    else:
        context['ipv6'] = False

    context.update(PerconaClusterHelper().parse_config())
    render(os.path.basename(resolve_cnf_file()),
           resolve_cnf_file(), context, perms=0o444)
def render_config_restart_on_changed(clustered, hosts, bootstrap=False):
    """Render mysql config and restart mysql service if file changes as a
    result.

    If bootstrap is True we do a bootstrap-pxc in order to bootstrap the
    percona cluster. This should only be performed once at cluster creation
    time.

    If percona is already bootstrapped we can get away with just ensuring that
    it is started so long as the new node to be added is guaranteed to have
    been restarted so as to apply the new config.
    """
    pre_hash = file_hash(resolve_cnf_file())
    render_config(clustered, hosts)
    create_binlogs_directory()
    update_db_rels = False
    if file_hash(resolve_cnf_file()) != pre_hash or bootstrap:
        if bootstrap:
            service('stop', 'mysql')
            service('bootstrap-pxc', 'mysql')
            # NOTE(dosaboy): this will not actually do anything if no cluster
            # relation id exists yet.
            notify_bootstrapped()
            update_db_rels = True
        else:
            delay = 1
            attempts = 0
            max_retries = 5
            # NOTE(dosaboy): avoid unnecessary restarts. Once mysql is started
            # it needn't be restarted when new units join the cluster since the
            # new units will join and apply their own config.
            if not seeded():
                action = service_restart
            else:
                action = service_start

            while not action('mysql'):
                if attempts == max_retries:
                    raise Exception("Failed to start mysql (max retries "
                                    "reached)")

                log("Failed to start mysql - retrying in %ss" % (delay),
                    WARNING)
                time.sleep(delay)
                delay += 2
                attempts += 1

        # If we get here we assume prior actions have succeeded to always
        # this unit is marked as seeded so that subsequent calls don't result
        # in a restart.
        mark_seeded()

        if update_db_rels:
            update_shared_db_rels()
    else:
        log("Config file '%s' unchanged", level=DEBUG)
def render_config_restart_on_changed(clustered, hosts, bootstrap=False):
    """Render mysql config and restart mysql service if file changes as a
    result.

    If bootstrap is True we do a bootstrap-pxc in order to bootstrap the
    percona cluster. This should only be performed once at cluster creation
    time.

    If percona is already bootstrapped we can get away with just ensuring that
    it is started so long as the new node to be added is guaranteed to have
    been restarted so as to apply the new config.
    """
    pre_hash = file_hash(resolve_cnf_file())
    render_config(clustered, hosts)
    update_db_rels = False
    if file_hash(resolve_cnf_file()) != pre_hash or bootstrap:
        if bootstrap:
            service('bootstrap-pxc', 'mysql')
            # NOTE(dosaboy): this will not actually do anything if no cluster
            # relation id exists yet.
            notify_bootstrapped()
            update_db_rels = True
        else:
            delay = 1
            attempts = 0
            max_retries = 5
            # NOTE(dosaboy): avoid unnecessary restarts. Once mysql is started
            # it needn't be restarted when new units join the cluster since the
            # new units will join and apply their own config.
            if not seeded():
                action = service_restart
            else:
                action = service_start

            while not action('mysql'):
                if attempts == max_retries:
                    raise Exception("Failed to start mysql (max retries "
                                    "reached)")

                log("Failed to start mysql - retrying in %ss" % (delay),
                    WARNING)
                time.sleep(delay)
                delay += 2
                attempts += 1

        # If we get here we assume prior actions have succeeded to always
        # this unit is marked as seeded so that subsequent calls don't result
        # in a restart.
        mark_seeded()

        if update_db_rels:
            update_shared_db_rels()
    else:
        log("Config file '%s' unchanged", level=DEBUG)
def render_config_restart_on_changed(hosts, bootstrap=False):
    """Render mysql config and restart mysql service if file changes as a
    result.

    If bootstrap is True we do a bootstrap-pxc in order to bootstrap the
    percona cluster. This should only be performed once at cluster creation
    time.

    If percona is already bootstrapped we can get away with just ensuring that
    it is started so long as the new node to be added is guaranteed to have
    been restarted so as to apply the new config.
    """
    config_file = resolve_cnf_file()
    pre_hash = file_hash(config_file)
    render_config(hosts)
    create_binlogs_directory()
    update_db_rels = False
    if file_hash(config_file) != pre_hash or bootstrap:
        if bootstrap:
            bootstrap_pxc()
            # NOTE(dosaboy): this will not actually do anything if no cluster
            # relation id exists yet.
            notify_bootstrapped()
            update_db_rels = True
        else:
            # NOTE(jamespage):
            # if mysql@bootstrap is running, then the native
            # bootstrap systemd service was used to start this
            # instance, and it was the initial seed unit
            # stop the bootstap version before restarting normal mysqld
            if service_running('mysql@bootstrap'):
                service_stop('mysql@bootstrap')

            attempts = 0
            max_retries = 5

            cluster_wait()
            while not service_restart('mysql'):
                if attempts == max_retries:
                    raise Exception("Failed to start mysql (max retries "
                                    "reached)")

                log("Failed to start mysql - retrying per distributed wait",
                    WARNING)
                attempts += 1
                cluster_wait()

        # If we get here we assume prior actions have succeeded to always
        # this unit is marked as seeded so that subsequent calls don't result
        # in a restart.
        mark_seeded()

        if update_db_rels:
            update_client_db_relations()
    else:
        log("Config file '{}' unchanged".format(config_file), level=DEBUG)
def render_config_restart_on_changed(hosts, bootstrap=False):
    """Render mysql config and restart mysql service if file changes as a
    result.

    If bootstrap is True we do a bootstrap-pxc in order to bootstrap the
    percona cluster. This should only be performed once at cluster creation
    time.

    If percona is already bootstrapped we can get away with just ensuring that
    it is started so long as the new node to be added is guaranteed to have
    been restarted so as to apply the new config.
    """
    config_file = resolve_cnf_file()
    pre_hash = file_hash(config_file)
    render_config(hosts)
    create_binlogs_directory()
    update_db_rels = False
    if file_hash(config_file) != pre_hash or bootstrap:
        if bootstrap:
            bootstrap_pxc()
            # NOTE(dosaboy): this will not actually do anything if no cluster
            # relation id exists yet.
            notify_bootstrapped()
            update_db_rels = True
        else:
            # NOTE(jamespage):
            # if mysql@bootstrap is running, then the native
            # bootstrap systemd service was used to start this
            # instance, and it was the initial seed unit
            # stop the bootstap version before restarting normal mysqld
            if service_running('mysql@bootstrap'):
                service_stop('mysql@bootstrap')

            attempts = 0
            max_retries = 5

            cluster_wait()
            while not service_restart('mysql'):
                if attempts == max_retries:
                    raise Exception("Failed to start mysql (max retries "
                                    "reached)")

                log("Failed to start mysql - retrying per distributed wait",
                    WARNING)
                attempts += 1
                cluster_wait()

        # If we get here we assume prior actions have succeeded to always
        # this unit is marked as seeded so that subsequent calls don't result
        # in a restart.
        mark_seeded()

        if update_db_rels:
            update_client_db_relations()
    else:
        log("Config file '{}' unchanged".format(config_file), level=DEBUG)
def render_config(hosts=None):
    if hosts is None:
        hosts = []

    config_file = resolve_cnf_file()
    if not os.path.exists(os.path.dirname(config_file)):
        os.makedirs(os.path.dirname(config_file))

    context = {
        'cluster_name': 'juju_cluster',
        'private_address': get_cluster_host_ip(),
        'cluster_hosts': ",".join(hosts),
        'sst_method': config('sst-method'),
        'sst_password': sst_password(),
        'innodb_file_per_table': config('innodb-file-per-table'),
        'table_open_cache': config('table-open-cache'),
        'lp1366997_workaround': config('lp1366997-workaround'),
        'binlogs_path': config('binlogs-path'),
        'enable_binlogs': config('enable-binlogs'),
        'binlogs_max_size': config('binlogs-max-size'),
        'binlogs_expire_days': config('binlogs-expire-days'),
        'performance_schema': config('performance-schema'),
        'is_leader': is_leader(),
        'server_id': get_server_id(),
    }

    if config('prefer-ipv6'):
        # NOTE(hopem): this is a kludge to get percona working with ipv6.
        # See lp 1380747 for more info. This is intended as a stop gap until
        # percona package is fixed to support ipv6.
        context['bind_address'] = '::'
        context['ipv6'] = True
    else:
        context['ipv6'] = False

    wsrep_provider_options = get_wsrep_provider_options()
    if wsrep_provider_options:
        context['wsrep_provider_options'] = wsrep_provider_options

    if CompareHostReleases(lsb_release()['DISTRIB_CODENAME']) < 'bionic':
        # myisam_recover is not valid for PXC 5.7 (introduced in Bionic) so we
        # only set it for PXC 5.6.
        context['myisam_recover'] = 'BACKUP'
        context['wsrep_provider'] = '/usr/lib/libgalera_smm.so'
    elif CompareHostReleases(lsb_release()['DISTRIB_CODENAME']) >= 'bionic':
        context['wsrep_provider'] = '/usr/lib/galera3/libgalera_smm.so'
        context['default_storage_engine'] = 'InnoDB'
        context['wsrep_log_conflicts'] = True
        context['innodb_autoinc_lock_mode'] = '2'
        context['pxc_strict_mode'] = config('pxc-strict-mode')

    context.update(PerconaClusterHelper().parse_config())
    render(os.path.basename(config_file), config_file, context, perms=0o444)
def ha_relation_joined(relation_id=None):
    cluster_config = get_hacluster_config()
    sstpsswd = sst_password()
    resources = {'res_percona': 'ocf:heartbeat:galera'}
    resource_params = {}
    vip_iface = (get_iface_for_address(cluster_config['vip'])
                 or config('vip_iface'))
    vip_cidr = (get_netmask_for_address(cluster_config['vip'])
                or config('vip_cidr'))

    if config('dns-ha'):
        update_dns_ha_resource_params(relation_id=relation_id,
                                      resources=resources,
                                      resource_params=resource_params)
        group_name = 'grp_{}_hostnames'.format(charm_name())
        groups = {group_name: 'res_{}_access_hostname'.format(charm_name())}

    if config('prefer-ipv6'):
        res_mysql_vip = 'ocf:heartbeat:IPv6addr'
        vip_params = 'params ipv6addr="%s" cidr_netmask="%s" nic="%s"' % \
                     (cluster_config['vip'], vip_cidr, vip_iface)
    else:
        res_mysql_vip = 'ocf:heartbeat:IPaddr2'
        vip_params = 'params ip="%s" cidr_netmask="%s" nic="%s"' % \
                     (cluster_config['vip'], vip_cidr, vip_iface)

    hostname_list = get_cluster_hostnames()
    percona_params = \
        ' params ' \
        ' wsrep_cluster_address="gcomm://' + ",".join(hostname_list) + '"' \
        ' config="' + resolve_cnf_file() + '"' \
        ' datadir="/var/lib/percona-xtradb-cluster"' \
        ' socket="/var/run/mysqld/mysqld.sock" ' \
        ' pid="/var/run/mysqld/mysqld.pid"' \
        ' check_user=sstuser check_passwd="' + sstpsswd + '"' \
        ' binary="/usr/bin/mysqld_safe"' \
        ' op monitor timeout=120 interval=20 depth=0' \
        ' op monitor role=Master timeout=120 interval=10 depth=0' \
        ' op monitor role=Slave timeout=120 interval=30 depth=0'

    percona_ms = {
        'ms_percona':
        'res_percona meta notify=true '
        'interleave=true master-max=3 '
        'ordered=true target-role=Started'
    }

    resource_params['res_percona'] = percona_params

    resources['res_mysql_vip'] = res_mysql_vip

    resource_params['res_mysql_vip'] = vip_params

    groups = {'grp_percona_cluster': 'res_mysql_vip'}

    colocations = {
        'colo_percona_cluster': '+inf: grp_percona_cluster ms_percona:Master'
    }

    for rel_id in relation_ids('ha'):
        relation_set(relation_id=rel_id,
                     corosync_bindiface=cluster_config['ha-bindiface'],
                     corosync_mcastport=cluster_config['ha-mcastport'],
                     resources=resources,
                     resource_params=resource_params,
                     ms=percona_ms,
                     groups=groups,
                     colocations=colocations)
def render_config(hosts=None):
    if hosts is None:
        hosts = []

    config_file = resolve_cnf_file()
    if not os.path.exists(os.path.dirname(config_file)):
        os.makedirs(os.path.dirname(config_file))

    context = {
        'cluster_name': 'juju_cluster',
        'private_address': get_cluster_host_ip(),
        'cluster_hosts': ",".join(hosts),
        'sst_method': config('sst-method'),
        'sst_password': sst_password(),
        'innodb_file_per_table': config('innodb-file-per-table'),
        'table_open_cache': config('table-open-cache'),
        'binlogs_path': config('binlogs-path'),
        'enable_binlogs': config('enable-binlogs'),
        'binlogs_max_size': config('binlogs-max-size'),
        'binlogs_expire_days': config('binlogs-expire-days'),
        'performance_schema': config('performance-schema'),
        'is_leader': is_leader(),
        'server_id': get_server_id(),
        'series_upgrade': is_unit_upgrading_set(),
    }

    if config('prefer-ipv6'):
        # NOTE(hopem): this is a kludge to get percona working with ipv6.
        # See lp 1380747 for more info. This is intended as a stop gap until
        # percona package is fixed to support ipv6.
        context['bind_address'] = '::'
        context['ipv6'] = True
    else:
        context['ipv6'] = False

    wsrep_provider_options = get_wsrep_provider_options()
    if wsrep_provider_options:
        context['wsrep_provider_options'] = wsrep_provider_options

    if config('wsrep-slave-threads') is not None:
        context['wsrep_slave_threads'] = config('wsrep-slave-threads')

    if CompareHostReleases(lsb_release()['DISTRIB_CODENAME']) < 'bionic':
        # myisam_recover is not valid for PXC 5.7 (introduced in Bionic) so we
        # only set it for PXC 5.6.
        context['myisam_recover'] = 'BACKUP'
        context['wsrep_provider'] = '/usr/lib/libgalera_smm.so'
        if 'wsrep_slave_threads' not in context:
            context['wsrep_slave_threads'] = 1
    elif CompareHostReleases(lsb_release()['DISTRIB_CODENAME']) >= 'bionic':
        context['wsrep_provider'] = '/usr/lib/galera3/libgalera_smm.so'
        context['default_storage_engine'] = 'InnoDB'
        context['wsrep_log_conflicts'] = True
        context['innodb_autoinc_lock_mode'] = '2'
        context['pxc_strict_mode'] = config('pxc-strict-mode')
        if 'wsrep_slave_threads' not in context:
            context['wsrep_slave_threads'] = 48

    if config('databases-to-replicate'):
        context['databases_to_replicate'] = get_databases_to_replicate()

    context['server-id'] = get_server_id()

    context.update(PerconaClusterHelper().parse_config())
    render(os.path.basename(config_file), config_file, context, perms=0o444)