def ha_relation_joined(relation_id=None):
    sstpsswd = sst_password()
    _relation_data = {
        'resources': {
            'res_mysql_monitor': 'ocf:percona:mysql_monitor'},
        'resource_params': {
            'res_mysql_monitor': RES_MONITOR_PARAMS % {'sstpass': sstpsswd}},
        'clones': {
            'cl_mysql_monitor': 'res_mysql_monitor meta interleave=true'},
        'delete_resources': ['loc_percona_cluster', 'grp_percona_cluster',
                             'res_mysql_vip']
    }

    if config('dns-ha'):
        update_hacluster_dns_ha('mysql', _relation_data)
        group_name = DNSHA_GROUP_NAME.format(service='mysql')
    else:
        update_hacluster_vip('mysql', _relation_data)
        group_name = VIP_GROUP_NAME.format(service='mysql')

    _relation_data['locations'] = {
        'loc_mysql': '{} rule inf: writable eq 1'.format(group_name)}
    _relation_data['colocations'] = {
        'colo_mysql': 'inf: {} cl_mysql_monitor'.format(group_name)}
    settings = {
        'json_{}'.format(k): json.dumps(v, **JSON_ENCODE_OPTIONS)
        for k, v in _relation_data.items() if v
    }

    for rel_id in relation_ids('ha'):
        relation_set(relation_id=rel_id, **settings)
def ha_relation_joined(relation_id=None):
    sstpsswd = sst_password()
    _relation_data = {
        'resources': {
            'res_mysql_monitor': 'ocf:percona:mysql_monitor'},
        'resource_params': {
            'res_mysql_monitor': RES_MONITOR_PARAMS % {'sstpass': sstpsswd}},
        'clones': {
            'cl_mysql_monitor': 'res_mysql_monitor meta interleave=true'},
        'delete_resources': ['loc_percona_cluster', 'grp_percona_cluster',
                             'res_mysql_vip']
    }

    if config('dns-ha'):
        update_hacluster_dns_ha('mysql', _relation_data)
        group_name = DNSHA_GROUP_NAME.format(service='mysql')
    else:
        update_hacluster_vip('mysql', _relation_data)
        group_name = VIP_GROUP_NAME.format(service='mysql')

    _relation_data['locations'] = {
        'loc_mysql': '{} rule inf: writable eq 1'.format(group_name)}
    _relation_data['colocations'] = {
        'colo_mysql': 'inf: {} cl_mysql_monitor'.format(group_name)}
    settings = {
        'json_{}'.format(k): json.dumps(v, **JSON_ENCODE_OPTIONS)
        for k, v in _relation_data.items() if v
    }

    for rel_id in relation_ids('ha'):
        relation_set(relation_id=rel_id, **settings)
Esempio n. 3
0
def ha_relation_joined(relation_id=None):
    cluster_config = get_hacluster_config()
    sstpsswd = sst_password()
    resources = {'res_mysql_monitor': 'ocf:percona:mysql_monitor'}
    resource_params = {'res_mysql_monitor':
                       RES_MONITOR_PARAMS % {'sstpass': sstpsswd}}

    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())}

    else:
        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('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)

        resources['res_mysql_vip'] = res_mysql_vip

        resource_params['res_mysql_vip'] = vip_params

        group_name = 'grp_percona_cluster'
        groups = {group_name: 'res_mysql_vip'}

    clones = {'cl_mysql_monitor': 'res_mysql_monitor meta interleave=true'}

    colocations = {'colo_percona_cluster': 'inf: {} cl_mysql_monitor'
                                           ''.format(group_name)}

    locations = {'loc_percona_cluster':
                 '{} rule inf: writable eq 1'
                 ''.format(group_name)}

    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,
                     groups=groups,
                     clones=clones,
                     colocations=colocations,
                     locations=locations)
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 install_percona_xtradb_cluster():
    '''Attempt PXC install based on seeding of passwords for users'''
    if pxc_installed():
        log('MySQL already installed, skipping')
        return

    _root_password = root_password()
    _sst_password = sst_password()
    if not _root_password or not _sst_password:
        log('Passwords not seeded, unable to install MySQL at this'
            ' point so deferring installation')
        return
    configure_mysql_root_password(_root_password)

    apt_install(determine_packages(), fatal=True)

    configure_sstuser(_sst_password)
    if config('harden') and 'mysql' in config('harden'):
        run_mysql_checks()
Esempio n. 6
0
def render_config(clustered=False, 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(),
        'clustered': clustered,
        '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'),
    }

    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(config_file),
           config_file, context, perms=0o444)
def upgrade():

    if is_leader():
        if is_unit_paused_set():
            log('Unit is paused, skiping upgrade', level=INFO)
            return

        # Leader sets on upgrade
        leader_set(**{'leader-ip': get_relation_ip('cluster')})
        configure_sstuser(sst_password())
        if not leader_get('root-password') and leader_get('mysql.passwd'):
            leader_set(**{'root-password': leader_get('mysql.passwd')})

        # On upgrade-charm we assume the cluster was complete at some point
        kvstore = kv()
        initial_clustered = kvstore.get(INITIAL_CLUSTERED_KEY, False)
        if not initial_clustered:
            kvstore.set(key=INITIAL_CLUSTERED_KEY, value=True)
            kvstore.flush()

        # broadcast the bootstrap-uuid
        wsrep_ready = get_wsrep_value('wsrep_ready') or ""
        if wsrep_ready.lower() in ['on', 'ready']:
            cluster_state_uuid = get_wsrep_value('wsrep_cluster_state_uuid')
            if cluster_state_uuid:
                mark_seeded()
                notify_bootstrapped(cluster_uuid=cluster_state_uuid)
    else:
        # Ensure all the peers have the bootstrap-uuid attribute set
        # as this is all happening during the upgrade-charm hook is reasonable
        # to expect the cluster is running.

        # Wait until the leader has set the
        try:
            update_bootstrap_uuid()
        except LeaderNoBootstrapUUIDError:
            status_set('waiting', "Waiting for bootstrap-uuid set by leader")
def upgrade():

    if is_leader():
        if is_unit_paused_set() or is_unit_upgrading_set():
            log('Unit is paused, skiping upgrade', level=INFO)
            return

        # Leader sets on upgrade
        leader_set(**{'leader-ip': get_relation_ip('cluster')})
        configure_sstuser(sst_password())
        if not leader_get('root-password') and leader_get('mysql.passwd'):
            leader_set(**{'root-password': leader_get('mysql.passwd')})

        # On upgrade-charm we assume the cluster was complete at some point
        kvstore = kv()
        initial_clustered = kvstore.get(INITIAL_CLUSTERED_KEY, False)
        if not initial_clustered:
            kvstore.set(key=INITIAL_CLUSTERED_KEY, value=True)
            kvstore.flush()

        # broadcast the bootstrap-uuid
        wsrep_ready = get_wsrep_value('wsrep_ready') or ""
        if wsrep_ready.lower() in ['on', 'ready']:
            cluster_state_uuid = get_wsrep_value('wsrep_cluster_state_uuid')
            if cluster_state_uuid:
                mark_seeded()
                notify_bootstrapped(cluster_uuid=cluster_state_uuid)
    else:
        # Ensure all the peers have the bootstrap-uuid attribute set
        # as this is all happening during the upgrade-charm hook is reasonable
        # to expect the cluster is running.

        # Wait until the leader has set the
        try:
            update_bootstrap_uuid()
        except LeaderNoBootstrapUUIDError:
            status_set('waiting', "Waiting for bootstrap-uuid set by leader")
def install_percona_xtradb_cluster():
    '''Attempt PXC install based on seeding of passwords for users'''
    if pxc_installed():
        log('MySQL already installed, skipping')
        return

    if not is_leader() and not is_leader_bootstrapped():
        log('Non-leader waiting on leader bootstrap, skipping percona install',
            DEBUG)
        return

    _root_password = root_password()
    _sst_password = sst_password()
    if not _root_password or not _sst_password:
        log('Passwords not seeded, unable to install MySQL at this'
            ' point so deferring installation')
        return
    configure_mysql_root_password(_root_password)

    apt_install(determine_packages(), fatal=True)

    configure_sstuser(_sst_password)
    if config('harden') and 'mysql' in config('harden'):
        run_mysql_checks()
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 series_upgrade():

    # Set this unit to series upgrading
    set_unit_upgrading()

    # The leader will "bootstrap" with no wrep peers
    # Non-leaders will point only at the newly upgraded leader until the
    # cluster series upgrade is completed.
    # Set cluster_series_upgrading for the duration of the cluster series
    # upgrade. This will be unset with the action
    # complete-cluster-series-upgrade on the leader node.
    if (leader_get('cluster_series_upgrade_leader') == get_relation_ip(
            'cluster')):
        hosts = []
    else:
        hosts = [leader_get('cluster_series_upgrade_leader')]

    # New series after series upgrade and reboot
    _release = lsb_release()['DISTRIB_CODENAME'].lower()

    if _release == "xenial":
        # Guarantee /var/run/mysqld exists
        _dir = '/var/run/mysqld'
        mkdir(_dir, owner="mysql", group="mysql", perms=0o755)

    # Install new versions of the percona packages
    apt_install(determine_packages())
    service_stop("mysql")

    if _release == "bionic":
        render_config(hosts)

    if _release == "xenial":
        # Move the packaged version empty DB out of the way.
        cmd = [
            "mv", "/var/lib/percona-xtradb-cluster",
            "/var/lib/percona-xtradb-cluster.dpkg"
        ]
        subprocess.check_call(cmd)

        # Symlink the previous versions data to the new
        cmd = ["ln", "-s", "/var/lib/mysql", "/var/lib/percona-xtradb-cluster"]
        subprocess.check_call(cmd)

    # Start mysql temporarily with no wrep for the upgrade
    cmd = ["mysqld"]
    if _release == "bionic":
        cmd.append("--skip-grant-tables")
        cmd.append("--user=mysql")
    cmd.append("--wsrep-provider=none")
    log("Starting mysqld --wsrep-provider='none' and waiting ...")
    proc = subprocess.Popen(cmd, stderr=subprocess.PIPE)

    # Wait for the mysql socket to exist
    check_for_socket(MYSQL_SOCKET, exists=True)

    # Execute the upgrade process
    log("Running mysql_upgrade")
    cmd = ['mysql_upgrade']
    if _release == "xenial":
        cmd.append('-p{}'.format(root_password()))
    subprocess.check_call(cmd)

    # Terminate the temporary mysql
    proc.terminate()

    # Wait for the mysql socket to be removed
    check_for_socket(MYSQL_SOCKET, exists=False)

    # Clear states
    clear_unit_paused()
    clear_unit_upgrading()

    if _release == "xenial":
        # Point at the correct my.cnf
        cmd = [
            "update-alternatives", "--set", "my.cnf",
            "/etc/mysql/percona-xtradb-cluster.cnf"
        ]
        subprocess.check_call(cmd)

    # Render config
    render_config(hosts)

    resume_unit_helper(register_configs())

    # finally update the sstuser if needed.
    # BUG: #1838044
    _sst_password = sst_password()
    if _sst_password:
        configure_sstuser(_sst_password)
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)