示例#1
0
def delete(cfg, username):
    """Delete MySQL backend user by username"""
    try:
        delete_user(cfg, username)
        LOG.info('User %s has deleted', username)
    except MySQLError as err:
        LOG.error('Failed to talk to database: %s', err)
    except (NoOptionError, NoSectionError) as err:
        LOG.error('Failed to parse config: %s', err)
        exit(1)
示例#2
0
def bug1258464killer(default_file):
    """
    bug1258464killer checks status of a local Galera node
    and if a) There are stuck COMMIT queries and b) There is an ALTER TABLE
    it will kill the node. This command workarounds a known bug
    https://bugs.launchpad.net/percona-xtradb-cluster/+bug/1258464
    """
    if default_file:
        if os.path.isfile(default_file):
            bug1258464(default_file)
        else:
            LOG.error('File not found : %s', default_file)
    else:
        bug1258464('/root/.my.cnf')
示例#3
0
def register_synced_backends(
        galera_cluster,
        proxysql,  # pylint: disable=too-many-arguments
        hostgroup_id,
        comment=None,
        limit=None,
        ignore_backend=None):
    """
    Find SYNCED node and register it as a backend.

    :param galera_cluster: GaleraCluster instance.
    :type galera_cluster: GaleraCluster
    :param proxysql: ProxySQL instance
    :type proxysql: ProxySQL
    :param hostgroup_id: hostgroup_id
    :type hostgroup_id: int
    :param comment: Optional comment to add to mysql_server
    :type comment: str
    :param limit: Register not more than limit number of backends
    :type limit: int
    :param ignore_backend: Do not register this backend
    :type ignore_backend: ProxySQLMySQLBackend
    """
    try:
        galera_nodes = galera_cluster.find_synced_nodes()

        if ignore_backend:
            node = GaleraNode(ignore_backend.hostname,
                              port=ignore_backend.port)
            LOG.debug('Ignoring backend %s', ignore_backend)
            if node in galera_nodes:
                LOG.debug('Remove %s from candidates', ignore_backend)
                galera_nodes.remove(node)

        if limit:
            candidate_nodes = galera_nodes[:limit]
        else:
            candidate_nodes = galera_nodes

        for galera_node in candidate_nodes:
            backend = ProxySQLMySQLBackend(galera_node.host,
                                           hostgroup_id=hostgroup_id,
                                           port=galera_node.port,
                                           comment=comment)
            proxysql.register_backend(backend)
            LOG.info('Added backend %s to hostgroup %d', backend, hostgroup_id)

    except GaleraClusterSyncedNodeNotFound as err:
        LOG.error(err)
示例#4
0
def main(ctx, cfg, debug, config, version):
    """proxysql-tool entrypoint"""
    if ctx.invoked_subcommand is None:
        if version:
            print(__version__)
            exit(0)
        else:
            print(ctx.get_help())
            exit(-1)

    setup_logging(LOG, debug=debug)

    if os.path.exists(config):
        cfg.read(config)
    else:
        LOG.error("Config file %s doesn't exist", config)
        exit(1)
示例#5
0
def bug1258464(default_file):
    """Check if node is affected by
    https://bugs.launchpad.net/percona-xtradb-cluster/+bug/1258464
    and if yes, kill the node.

    :param default_file: Path to my.cnf
    :return: True if it killed the node.
    :rtype: bool
    """
    try:
        connection = pymysql.connect(read_default_file=default_file)
        with connection.cursor() as cursor:
            cursor.execute("SELECT COUNT(*)"
                           "FROM information_schema.processlist "
                           "WHERE State = 'wsrep in pre-commit stage' "
                           "AND Info = 'COMMIT'")
            count_pre_commit = cursor.fetchone()[0]
            cursor.execute("SELECT COUNT(*)"
                           "FROM information_schema.processlist "
                           "WHERE State = "
                           "'Waiting for table metadata lock' "
                           "AND Info LIKE 'ALTER TABLE%'")
            count_waiting = cursor.fetchone()[0]
            if count_pre_commit > 100 and count_waiting > 0:
                my_cnf_path = get_my_cnf()
                pid = get_pid(my_cnf_path)
                kill_process(pid)
                return True
    except OperationalError as err:
        LOG.error(str(err))
    except OSError as err:
        LOG.error(str(err))
    except NotImplementedError as err:
        LOG.error(str(err))
    return False
 def find_synced_nodes(self):
     """Find a node in the cluster in SYNCED state.
     :return: List of Galera node in SYNCED state.
     :rtype: list(GaleraNode)
     :raise: GaleraClusterSyncedNodeNotFound
     """
     LOG.debug('Looking for a SYNCED node')
     nodes = []
     for galera_node in self._nodes:
         try:
             state = galera_node.wsrep_local_state
             LOG.debug('%s state: %s', galera_node, state)
             if state == GaleraNodeState.SYNCED:
                 nodes.append(galera_node)
         except OperationalError as err:
             LOG.error(err)
             LOG.info('Skipping node %s', galera_node)
     if nodes:
         return nodes
     else:
         raise GaleraClusterSyncedNodeNotFound('Cluster has '
                                               'no SYNCED nodes')
示例#7
0
def set_password(cfg, username, password):
    """Change password of exists MySQL user"""
    try:
        change_password(cfg, username, password)
        LOG.info('Password for user %s changed', username)
    except ProxySQLUserNotFound:
        LOG.error("User not found")
        exit(1)
    except MySQLError as err:
        LOG.error('Failed to talk to database: %s', err)
    except (NoOptionError, NoSectionError) as err:
        LOG.error('Failed to parse config: %s', err)
        exit(1)
    except ProxySQLBackendNotFound as err:
        LOG.error('ProxySQL backends not found: %s', err)
        exit(1)
示例#8
0
def set_sync(cfg, ip_address, port):
    """Set server to sync state."""

    try:
        server_set_wsrep_desync(cfg, ip_address, port, wsrep_desync='OFF')
    except NotImplementedError as err:
        LOG.error(err)
        exit(1)
    except (NoOptionError, NoSectionError) as err:
        LOG.error('Failed to parse config: %s', err)
        exit(1)

    except MySQLError as err:
        LOG.error('Failed to talk to database: %s', err)
    except ProxySQLBackendNotFound as err:
        LOG.error(err)
示例#9
0
def create(
        cfg,
        username,
        password,
        active,
        use_ssl,  # pylint: disable=too-many-arguments
        default_hostgroup,
        default_schema,
        schema_locked,
        transaction_persistent,
        fast_forward,
        backend,
        frontend,
        max_connections):
    """Add user of MySQL backend to ProxySQL"""
    kwargs = {
        'username': username,
        'password': password,
        'use_ssl': use_ssl,
        'active': active,
        'default_hostgroup': default_hostgroup,
        'default_schema': default_schema,
        'schema_locked': schema_locked,
        'transaction_persistent': transaction_persistent,
        'backend': backend,
        'frontend': frontend,
        'fast_forward': fast_forward,
        'max_connections': max_connections
    }
    try:
        create_user(cfg, kwargs)
        LOG.info('User %s successfully created', username)
    except MySQLError as err:
        LOG.error('Failed to talk to database: %s', err)
    except (NoOptionError, NoSectionError) as err:
        LOG.error('Failed to parse config: %s', err)
        exit(1)
示例#10
0
def modify(ctx, cfg, username):
    """Modify MySQL backend user by username"""
    try:
        modify_user(cfg, username, ctx.args)
        LOG.info("User %s has modified", username)
    except ProxySQLUserNotFound:
        LOG.error("User not found")
        exit(1)
    except MySQLError as err:
        LOG.error('Failed to talk to database: %s', err)
    except ValueError:
        LOG.error("Invalid input")
        exit(1)
示例#11
0
def status(cfg):
    """Show status of MySQL backends."""

    try:
        server_status(cfg)
    except NotImplementedError as err:
        LOG.error(err)
        exit(1)
    except (NoOptionError, NoSectionError) as err:
        LOG.error('Failed to parse config: %s', err)
        exit(1)

    except MySQLError as err:
        LOG.error('Failed to talk to database: %s', err)
示例#12
0
def register(cfg):
    """Registers Galera cluster nodes with ProxySQL."""

    try:
        galera_register(cfg)
    except NotImplementedError as err:
        LOG.error(err)
        exit(1)
    except (NoOptionError, NoSectionError) as err:
        LOG.error('Failed to parse config: %s', err)
        exit(1)

    except MySQLError as err:
        LOG.error('Failed to talk to database: %s', err)
示例#13
0
def aws_notify_master(cfg):
    """The function moves network interface to local instance and brings it up.
    Steps:

    - Detach network interface if attached to anywhere.
    - Attach the network interface to the local instance.
    - Configure IP address on this instance

    :param cfg: config object
    """
    try:
        os.environ['AWS_ACCESS_KEY_ID'] = cfg.get('aws', 'aws_access_key_id')
        os.environ['AWS_SECRET_ACCESS_KEY'] = cfg.get('aws',
                                                      'aws_secret_access_key')
        os.environ['AWS_DEFAULT_REGION'] = cfg.get('aws', 'aws_default_region')
    except NoOptionError:
        LOG.error('aws_access_key_id, aws_secret_access_key and '
                  'aws_default_region must be defined in '
                  'aws section of the config file.')
        exit(-1)

    instance_id = get_my_instance_id()
    try:
        ip = cfg.get('proxysql', 'virtual_ip')
        netmask = cfg.get('proxysql', 'virtual_netmask')

        network_interface = get_network_interface(ip)

        if network_interface_attached(network_interface):
            detach_network_interface(network_interface)

        local_interface = "eth%d" % DEVICE_INDEX
        ensure_local_interface_is_gone(local_interface)

        ensure_network_interface_is_detached(network_interface)

        attach_network_interface(network_interface, instance_id)

        configure_local_interface(local_interface, ip, netmask)
    except NoOptionError as err:
        LOG.error('virtual_ip and virtual_netmask must be defined in '
                  'proxysql section of the config file.')
        LOG.error(err)
示例#14
0
def check_backend(
        backend,
        galera_cluster,
        proxysql,
        hostgroup_id,
        comment,  # pylint: disable=too-many-arguments
        limit=None,
        ignore_backend=None,
        recovered_hostgroup_id=None,
        recoverd_comment=None):
    """
    Check health of given backed and if necessary replace it.

    :param backend: MySQL backend.
    :type backend: ProxySQLMySQLBackend
    :param galera_cluster: GaleraCluster instance.
    :type galera_cluster: GaleraCluster
    :param proxysql: ProxySQL instance.
    :type proxysql: ProxySQL
    :param hostgroup_id: Hostgroup_id which the backend belongs to.
    :type hostgroup_id: int
    :param comment: Comment to add to mysql_servers in ProxySQL
    :param limit: Register not more than limit number of backends
    :type limit: int
    :param ignore_backend: Do not register this backend
    :type ignore_backend: ProxySQLMySQLBackend
    :param recovered_hostgroup_id: If backend recovers from OFFLINE_SOFT assign
        it to this hostgroup_id. Default hostgroup_id.
    :type recovered_hostgroup_id: int
    :param recoverd_comment: If backend recovers from OFFLINE_SOFT set
        this comment
    :type recoverd_comment: str
    :return: True if backend successfully registered.
    :rtype: bool
    """
    # check it
    LOG.debug('Backend %s is already registered', backend)
    LOG.debug('Checking its health')
    try:
        node = galera_cluster.find_node(backend.hostname, backend.port)

        state = node.wsrep_local_state
        LOG.debug('%s state: %d', node, state)

        if state == GaleraNodeState.SYNCED:
            LOG.debug('Node %s (%s) is healthy', node, backend.status)

            if backend.status != BackendStatus.online:

                LOG.debug('Deregistering %s (%s)', backend, backend.status)
                proxysql.deregister_backend(backend)

                backend.status = BackendStatus.online

                if not recovered_hostgroup_id:
                    recovered_hostgroup_id = hostgroup_id
                backend.hostgroup_id = recovered_hostgroup_id

                if recoverd_comment:
                    backend.comment = recoverd_comment

                LOG.debug('Registering %s (%s)', backend, backend.status)
                proxysql.register_backend(backend)
        else:
            LOG.warn(
                'Node %s is reachable but unhealty, '
                'setting it OFFLINE_SOFT', node)
            proxysql.set_status(backend, BackendStatus.offline_soft)
            register_synced_backends(galera_cluster,
                                     proxysql,
                                     hostgroup_id,
                                     comment=comment,
                                     limit=limit,
                                     ignore_backend=ignore_backend)

    except GaleraClusterNodeNotFound:
        LOG.warn('Backend %s is not a cluster member. Deregistering it.',
                 backend)
        proxysql.deregister_backend(backend)
        register_synced_backends(galera_cluster,
                                 proxysql,
                                 hostgroup_id,
                                 comment=comment,
                                 limit=limit,
                                 ignore_backend=ignore_backend)
    except OperationalError as err:
        LOG.error(err)
        LOG.error('Looks like backend %s is unhealthy. Deregistering it.',
                  backend)
        proxysql.deregister_backend(backend)
        register_synced_backends(galera_cluster,
                                 proxysql,
                                 hostgroup_id,
                                 comment=comment,
                                 limit=limit,
                                 ignore_backend=ignore_backend)
    return True