예제 #1
0
def leave(server_info, **kwargs):
    """Removes a server from a Group Replication group.

    :param server_info: Connection information
    :type  server_info: dict | Server | str
    :param kwargs:      Keyword arguments:
                        dry_run:    Do not make changes to the server
                        option_file: The path to a options file to check
                                     configuration.
                        skip_backup: if True, skip the creation of a backup
                                     file when modifying the options file.
    :type kwargs:       dict

    :raise GadgetError: If the given server is not a member of a Group
                        Replication

    :return: True if the server stop_gr_plugin command was executed otherwise
             False.
    :rtype: boolean
    """

    # get the server instance
    server = get_server(server_info=server_info)

    dry_run = kwargs.get("dry_run", False)
    option_file = kwargs.get("option_file", None)
    skip_backup = kwargs.get("skip_backup", False)

    if server is None:
        raise GadgetError(_ERROR_NO_SERVER)

    msg = _RUNNING_COMMAND.format(LEAVE, server)
    _LOGGER.info("")
    _LOGGER.log(STEP_LOG_LEVEL_VALUE, msg)

    try:
        # verify server status (is in a group?)
        if is_member_of_group(server) and is_active_member(server):
            _LOGGER.log(STEP_LOG_LEVEL_VALUE, "Attempting to leave from the "
                        "Group Replication group...")
            if not dry_run:
                stop_gr_plugin(server)
            _LOGGER.info("Server state: %s", get_member_state(server))
            _LOGGER.log(STEP_LOG_LEVEL_VALUE, "Server %s has left the group.",
                        server)

            # Update the Group Replication options on defaults file.
            # Note: Set group_replication_start_on_boot=ON
            if option_file is not None and option_file != "":
                persist_gr_config(option_file, None, set_on=False,
                                  dry_run=dry_run, skip_backup=skip_backup)

            return True

        # server is not active
        elif is_member_of_group(server):
            _LOGGER.warning("The server %s is not actively replicating.",
                            server)
            _LOGGER.info("Server state: %s", get_member_state(server))
            _LOGGER.log(STEP_LOG_LEVEL_VALUE, "Server %s is "
                        "not active in the group.", server)

            # Update the group_replication_start_on_boot option on defaults
            # file.
            # Note: Set group_replication_start_on_boot=OFF
            if option_file is not None and option_file != "":
                persist_gr_config(option_file, None, set_on=False,
                                  dry_run=dry_run, skip_backup=skip_backup)

        # the server has not been configured for GR
        else:
            raise GadgetError(_ERROR_NOT_A_MEMBER.format(server))

        if dry_run:
            _LOGGER.warning(_WARN_DRY_RUN_USED)

    finally:
        # disconnect servers.
        if server is not None:
            server.disconnect()

    return False
예제 #2
0
def join(server_info, peer_server_info, **kwargs):
    """Add a server to an existing Group Replication group.

    The contact point to add the new server to the group replication is the
    specified peer server, which must be already a member of the group.

    :param server_info: Connection information
    :type  server_info: dict | Server | str
    :param peer_server_info: Connection information of a server member of a
                             Group Replication group.
    :type  peer_server_info: dict | Server | str
    :param kwargs:  Keyword arguments:
                        gr_address: The host:port that the gcs plugin uses to
                                    other servers communicate with this server.
                        dry_run:    Do not make changes to the server
                        verbose:    If the command should show more verbose
                                    information.
                        option_file: The path to a options file to check
                                     configuration.
                        skip_backup: if True, skip the creation of a backup
                                     file when modifying the options file.
                        ssl_mode: SSL mode to be used with group replication.
                                  (Note server GR SSL modes need
                                  to be consistent with the SSL GR modes on the
                                  peer-server otherwise an error will be
                                  thrown).
                        skip_rpl_user: If True, skip the creation of the
                                       replication user.
    :type kwargs:   dict

    :raise GadgetError:         If server_info or peer_server_info is None.
                                If the given peer_server_info is from a server
                                that is not a member of Group Replication.
    :raise GadgetCnxInfoError:  If the connection information on server_info
                                or peer_server_info could not be parsed.
    :raise GadgetServerError:   If a connection fails.

    :return: True if the start_gr_plugin method was executed otherwise False.
    :rtype: boolean
    """

    verbose = kwargs.get("verbose", False)
    dry_run = kwargs.get("dry_run", False)
    gr_host = kwargs.get("gr_host", None)
    option_file = kwargs.get("option_file", None)
    skip_backup = kwargs.get("skip_backup", False)
    # Default is value for ssl_mode is REQUIRED
    ssl_mode = kwargs.get("ssl_mode", GR_SSL_REQUIRED)
    skip_rpl_user = kwargs.get("skip_rpl_user", False)

    # Connect to the server
    server = get_server(server_info=server_info)
    if server is None:
        raise GadgetError(_ERROR_NO_SERVER)

    msg = _RUNNING_COMMAND.format(JOIN, server)
    _LOGGER.info("")
    _LOGGER.log(STEP_LOG_LEVEL_VALUE, msg)

    _LOGGER.log(STEP_LOG_LEVEL_VALUE, "Checking Group Replication "
                                      "prerequisites.")

    peer_server = get_server(server_info=peer_server_info)

    # Connect to the peer server
    if peer_server is None:
        if server is not None:
            server.disconnect()
        raise GadgetError("No peer server provided. It is required to get "
                          "information from the group.")

    try:
        # verify the peer server belong to a GR group.
        if not is_member_of_group(peer_server):
            raise GadgetError("Peer server '{0}' is not a member of a GR "
                              "group.".format(peer_server))

        # verify the server status is ONLINE
        peer_server_state = get_member_state(peer_server)
        if peer_server_state != 'ONLINE':
            raise GadgetError("Cannot join instance {0}. Peer instance {1} "
                              "state is currently '{2}', but is expected to "
                              "be 'ONLINE'.".format(server, peer_server,
                                                    peer_server_state))

        # Throw an error in case server doesn't support SSL and the ssl_mode
        # option was provided a value other than DISABLED
        if server.select_variable(HAVE_SSL) != 'YES' and \
           ssl_mode != GR_SSL_DISABLED:
            raise GadgetError(_ERROR_NO_HAVE_SSL.format(server))

        # Throw an error in case there is any SSL incompatibilities are found
        # on the peer server or if the peer-server is incompatible with the
        # value of the ssl_mode option.
        check_peer_ssl_compatibility(peer_server, ssl_mode)

        if not skip_rpl_user:
            rpl_user_dict = get_rpl_usr(kwargs)

            req_dict = get_req_dict(server, rpl_user_dict["replication_user"],
                                    peer_server, option_file=option_file)
        else:
            # if replication user is to be skipped, no need to add the
            # replication user to the requirements list
            req_dict = get_req_dict(server, None, peer_server,
                                    option_file=option_file)
            rpl_user_dict = None

        check_server_requirements(server, req_dict, rpl_user_dict, verbose,
                                  dry_run, skip_backup=skip_backup)

        # verify the group replication is installed and not disabled.
        check_gr_plugin_is_installed(server, option_file, dry_run)

        # Initialize log error access and get current position in it
        error_log_size = None
        if server.is_alias("127.0.0.1"):
            error_log = LocalErrorLog(server)
            try:
                error_log_size = error_log.get_size()
            except Exception as err:  # pylint: disable=W0703
                _LOGGER.warning(
                    "Unable to access the server error log: %s", str(err))
        else:
            _LOGGER.warning("Not running locally on the server and can not "
                            "access its error log.")

        # verify the server does not belong already to a GR group.
        if is_active_member(server):
            health(server, **kwargs)
            raise GadgetError(_ERROR_ALREADY_A_MEMBER.format(server, JOIN))

        gr_host, local_port = resolve_gr_local_address(gr_host, server.host,
                                                       server.port)

        local_address = "{0}:{1}".format(gr_host, local_port)
        _LOGGER.debug("local_address to use: %s", local_address)

        # Get local_address from the peer server to add to the list of
        # group_seeds.
        peer_local_address = get_gr_local_address_from(peer_server)

        option_parser = req_dict.get(OPTION_PARSER, None)
        gr_config_vars = get_gr_config_vars(local_address, kwargs,
                                            option_parser, peer_local_address)

        # Do several replication user related tasks if the
        # skip-replication-user option was not provided
        if not skip_rpl_user:
            # The replication user for be check/create on the peer server.
            # NOTE: rpl_user_dict["host"] has the FQDN resolved from the host
            # provided by the user
            replication_user = "******".format(
                rpl_user_dict["recovery_user"], rpl_user_dict["host"])
            rpl_user_dict["replication_user"] = replication_user

            # Check the given replication user exists on peer
            req_dict_user = get_req_dict_user_check(peer_server,
                                                    replication_user)

            # Check and create the given replication user on peer server.
            # NOTE: No other checks will be performed, only the replication
            # user.
            check_server_requirements(peer_server, req_dict_user,
                                      rpl_user_dict, verbose, dry_run,
                                      skip_schema_checks=True)

        # IF the group name is not set, try to acquire it from a peer server.
        if gr_config_vars[GR_GROUP_NAME] is None:
            _LOGGER.debug("Trying to retrieve group replication name from "
                          "peer server.")
            group_name = get_gr_name_from_peer(peer_server)

            _LOGGER.debug("Retrieved group replication name from peer"
                          " server: %s.", group_name)
            gr_config_vars[GR_GROUP_NAME] = group_name

        # Set the single_primary mode according to the value set on peer
        # server
        if gr_config_vars[GR_SINGLE_PRIMARY_MODE] is None:
            gr_config_vars[GR_SINGLE_PRIMARY_MODE] = \
                get_gr_variable_from_peer(peer_server, GR_SINGLE_PRIMARY_MODE)

        if gr_config_vars[GR_GROUP_NAME] is None:
            raise GadgetError(
                _ERROR_UNABLE_TO_GET.format("Group Replication group name",
                                            peer_server))

        _LOGGER.log(STEP_LOG_LEVEL_VALUE,
                    "Joining Group Replication group: %s",
                    gr_config_vars[GR_GROUP_NAME])

        if gr_config_vars[GR_GROUP_SEEDS] is None:
            raise GadgetError(
                _ERROR_UNABLE_TO_GET.format("peer addresses", peer_server))

        # Remove IP whitelist variable if not set (by user or from the option
        # file) to use the default server value and not set it with None.
        if gr_config_vars[GR_IP_WHITELIST] is None:
            gr_config_vars.pop(GR_IP_WHITELIST)

        setup_gr_config(server, gr_config_vars, dry_run=dry_run)

        if not skip_rpl_user:
            # if the skip replication user option was not specified,
            # run the change master to store MySQL replication user name or
            # password information in the master info repository
            do_change_master(server, rpl_user_dict, dry_run=dry_run)

        _LOGGER.log(STEP_LOG_LEVEL_VALUE,
                    "Attempting to join to Group Replication group...")

        try:
            start_gr_plugin(server, dry_run)
            _LOGGER.log(STEP_LOG_LEVEL_VALUE, "Server %s joined "
                        "Group Replication group %s.", server,
                        gr_config_vars[GR_GROUP_NAME])

        except:
            _LOGGER.error("\nGroup Replication join failed.")
            if error_log_size is not None:
                log_data = error_log.read(error_log_size)
                if log_data:
                    _LOGGER.error("Group Replication plugin failed to start. "
                                  "Server error log contains the following "
                                  "errors: \n %s", log_data)
            raise

        # Update the Group Replication options on defaults file.
        # Note: Set group_replication_start_on_boot=ON
        if OPTION_PARSER in req_dict.keys():
            persist_gr_config(req_dict[OPTION_PARSER], gr_config_vars,
                              dry_run=dry_run, skip_backup=skip_backup)

        if dry_run:
            _LOGGER.warning(_WARN_DRY_RUN_USED)
            return False

    finally:
        # disconnect servers.
        if server is not None:
            server.disconnect()
        if peer_server is not None:
            peer_server.disconnect()

    return True
예제 #3
0
def start(server_info, **kwargs):
    """Start a group replication group with the given server information.

    :param server_info: Connection information
    :type  server_info: dict | Server | str
    :param kwargs:      Keyword arguments:
                        gr_address: The host:port that the gcs plugin uses to
                                    other servers communicate with this server.
                        dry_run:    Do not make changes to the server
                        verbose:    If the command should show more verbose
                                    information.
                        option_file: The path to a options file to check
                                     configuration.
                        skip_backup: if True, skip the creation of a backup
                                     file when modifying the options file.
                        skip_schema_checks: Skip schema validation.
                        ssl_mode: SSL mode to be used with group replication.
    :type kwargs:       dict

    :raise GadgetError:         If server_info is None.
    :raise GadgetCnxInfoError:  If the connection information on
                                server_info could not be parsed.
    :raise GadgetServerError:   If a connection fails.

    :return: True if the start_gr_plugin method was executed otherwise False.
    :rtype: boolean
    """

    server = get_server(server_info=server_info)
    if server is None:
        raise GadgetError(_ERROR_NO_SERVER)
    msg = _RUNNING_COMMAND.format(START, server)
    _LOGGER.info("")
    _LOGGER.log(STEP_LOG_LEVEL_VALUE, msg)

    verbose = kwargs.get("verbose", False)
    dry_run = kwargs.get("dry_run", False)
    gr_host = kwargs.get("gr_address", None)
    skip_schema_checks = kwargs.get("skip_schema_checks", [])
    option_file = kwargs.get("option_file", None)
    skip_backup = kwargs.get("skip_backup", False)

    _LOGGER.log(STEP_LOG_LEVEL_VALUE, "Checking Group Replication "
                                      "prerequisites.")
    try:
        # Throw an error in case server doesn't support SSL and the ssl_mode
        # option was provided a value other than DISABLED
        if server.select_variable(HAVE_SSL) != 'YES' and \
                kwargs["ssl_mode"] != GR_SSL_DISABLED:
            raise GadgetError(_ERROR_NO_HAVE_SSL.format(server))

        rpl_user_dict = get_rpl_usr(kwargs)

        req_dict = get_req_dict(server, rpl_user_dict["replication_user"],
                                option_file=option_file)

        check_server_requirements(server, req_dict, rpl_user_dict, verbose,
                                  dry_run, skip_schema_checks,
                                  skip_backup=skip_backup)

        gr_host, local_port = resolve_gr_local_address(gr_host, server.host,
                                                       server.port)

        # verify the group replication is not Disabled in the server.
        check_gr_plugin_is_installed(server, option_file, dry_run)

        # verify the server does not belong already to a GR group.
        if is_active_member(server):
            health(server, **kwargs)
            raise GadgetError(_ERROR_ALREADY_A_MEMBER.format(server, START))

        local_address = "{0}:{1}".format(gr_host, local_port)

        option_parser = req_dict.get(OPTION_PARSER, None)
        gr_config_vars = get_gr_config_vars(local_address, kwargs,
                                            option_parser)

        if gr_config_vars[GR_GROUP_SEEDS] is None:
            gr_config_vars.pop(GR_GROUP_SEEDS)

        # Remove IP whitelist variable if not set (by user or from the option
        # file) to use the default server value and not set it with None.
        if gr_config_vars[GR_IP_WHITELIST] is None:
            gr_config_vars.pop(GR_IP_WHITELIST)

        if gr_config_vars[GR_GROUP_NAME] is None:
            new_uuid = get_group_uuid_name(server)
            _LOGGER.debug("A new UUID has been generated for the group "
                          "replication name %s", new_uuid)
            gr_config_vars[GR_GROUP_NAME] = new_uuid

        # Set the single_primary mode, if no value given then set ON as
        # default.
        if gr_config_vars[GR_SINGLE_PRIMARY_MODE] is None:
            gr_config_vars[GR_SINGLE_PRIMARY_MODE] = '"ON"'

        _LOGGER.log(STEP_LOG_LEVEL_VALUE, "Group Replication group name: %s",
                    gr_config_vars[GR_GROUP_NAME])

        setup_gr_config(server, gr_config_vars, dry_run=dry_run)

        # Run the change master to store MySQL replication user name or
        # password information in the master info repository
        do_change_master(server, rpl_user_dict, dry_run=dry_run)

        set_bootstrap(server, dry_run)

        _LOGGER.log(STEP_LOG_LEVEL_VALUE,
                    "Attempting to start the Group Replication group...")

        # Attempt to start the Group Replication plugin
        start_gr_plugin(server, dry_run)

        # Wait for the super_read_only to be unset.
        super_read_only = server.select_variable("super_read_only", 'global')
        _LOGGER.debug("super_read_only: %s", super_read_only)
        waiting_time = 0
        informed = False
        while int(super_read_only) and waiting_time < TIME_OUT:
            time.sleep(WAIT_SECONDS)
            waiting_time += WAIT_SECONDS
            _LOGGER.debug("have been waiting %s seconds", waiting_time)
            # inform what are we waiting for
            if waiting_time >= 10 and not informed:
                _LOGGER.info("Waiting for super_read_only to be unset.")
                informed = True

            super_read_only = server.select_variable("super_read_only",
                                                     'global')
            _LOGGER.debug("super_read_only: %s", super_read_only)

        if int(super_read_only):
            raise GadgetError("Timeout waiting for super_read_only to be "
                              "unset after call to start Group Replication "
                              "plugin.")

        _LOGGER.log(STEP_LOG_LEVEL_VALUE,
                    "Group Replication started for group: %s.",
                    gr_config_vars[GR_GROUP_NAME])

        unset_bootstrap(server, dry_run)

        # Update the Group Replication options on defaults file.
        # Note: Set group_replication_start_on_boot=ON
        if OPTION_PARSER in req_dict.keys():
            persist_gr_config(req_dict[OPTION_PARSER], gr_config_vars,
                              dry_run=dry_run, skip_backup=skip_backup)

        if dry_run:
            _LOGGER.warning(_WARN_DRY_RUN_USED)
            return False

    finally:
        # Disconnect the server prior to end method invocation.
        if server is not None:
            server.disconnect()

    return True
예제 #4
0
def check(**kwargs):
    """Check and update instance configurations relative to Group Replication.

    :param kwargs:    Keyword arguments:
                        update:     Make changes to the options file.
                        verbose:    If the command should show more verbose
                                    information.
                        option_file: The path to a options file to check
                                     configuration.
                        skip_backup: if True, skip the creation of a backup
                                     file when modifying the options file.
                        server:     Connection information (dict | Server |
                                    str)
    :type kwargs:     dict

    :raise GadgetError:  If the file cannot be updated.

    :return: True if the options file was modified.
    :rtype: boolean
    """

    _LOGGER.info("")
    verbose = kwargs.get("verbose", False)
    update = kwargs.get("update", False)
    option_file = kwargs.get("option_file", None)
    skip_backup = kwargs.get("skip_backup", False)
    server_info = kwargs.get("server", None)

    # Get the server instance
    server = get_server(server_info=server_info)

    # This method requires at least one of the server or the option file.
    if (option_file is None or option_file == "") and server is None:
        raise GadgetError("Either the server or the defaults file was not "
                          "given.")

    if option_file:
        msg = "Running {0} command for file '{1}'.".format(CHECK, option_file)
    else:
        msg = "Running {0} command.".format(CHECK)
    _LOGGER.log(STEP_LOG_LEVEL_VALUE, msg)

    try:
        # if server already belongs to a group and the update option
        # was provided, dump its GR configurations to the option file
        if is_member_of_group(server) and is_active_member(server):
            gr_configs = get_gr_configs_from_instance(server)
            if update:
                # the variable comes from the server, no need to test for
                # loose prefix variant.
                if not gr_configs.get("group_replication_group_seeds", None):
                    _LOGGER.warning(
                        "The 'group_replication_group_seeds' is not defined "
                        "on %s. This option is mandatory to allow the server "
                        "to automatically rejoin the cluster after reboot. "
                        "Please manually update its value on option file "
                        "'%s'.", str(server), option_file)
                _LOGGER.info("Updating option file '%s' with Group "
                             "Replication settings from "
                             "%s", option_file, str(server))
                persist_gr_config(option_file, gr_configs)
                result = True
            else:
                result = False
        # If the server doesn't belong to any group, check if it meets GR
        # requirements, printing to stdout what needs to be changed and
        # update the configuration file if provided
        else:
            # The dictionary with the requirements to be verified.
            if server is not None:
                req_dict = get_req_dict(server, None, peer_server=None,
                                        option_file=option_file)
                skip_schema_checks = False
            else:
                req_dict = get_req_dict_for_opt_file(option_file)
                skip_schema_checks = True

            _LOGGER.log(STEP_LOG_LEVEL_VALUE, "Checking Group Replication "
                        "prerequisites.")

            # set dry_run to avoid changes on server as replication user
            # creation.
            result = check_server_requirements(
                server, req_dict, None, verbose=verbose, dry_run=True,
                skip_schema_checks=skip_schema_checks, update=update,
                skip_backup=skip_backup)

            # verify the group replication is installed and not disabled.
            if server is not None:
                check_gr_plugin_is_installed(server, option_file,
                                             dry_run=(not update))

    finally:
        # Disconnect the server prior to end method invocation.
        if server is not None:
            server.disconnect()

    return result
예제 #5
0
def join(server_info, peer_server_info, **kwargs):
    """Add a server to an existing Group Replication group.

    The contact point to add the new server to the group replication is the
    specified peer server, which must be already a member of the group.

    :param server_info: Connection information
    :type  server_info: dict | Server | str
    :param peer_server_info: Connection information of a server member of a
                             Group Replication group.
    :type  peer_server_info: dict | Server | str
    :param kwargs:  Keyword arguments:
                        gr_address: The host:port that the gcs plugin uses to
                                    other servers communicate with this server.
                        dry_run:    Do not make changes to the server
                        verbose:    If the command should show more verbose
                                    information.
                        option_file: The path to a options file to check
                                     configuration.
                        skip_backup: if True, skip the creation of a backup
                                     file when modifying the options file.
                        ssl_mode: SSL mode to be used with group replication.
                                  (Note server GR SSL modes need
                                  to be consistent with the SSL GR modes on the
                                  peer-server otherwise an error will be
                                  thrown).
                        failover_consistency: Group Replication failover
                                              Consistency, must be a string
                                              containing either
                                              "BEFORE_ON_PRIMARY_FAILOVER",
                                              "EVENTUAL", "0" or "1".
                        expel_timeout: Group Replication member expel Timeout.
                                       Must must be an integer value
                                       containing the time in seconds to wait
                                       before ghe killer node expels members
                                       suspected of having failed from the
                                       group.
                        skip_rpl_user: If True, skip the creation of the
                                       replication user.
                        target_is_local: Target is running in the same host
    :type kwargs:   dict

    :raise GadgetError:         If server_info or peer_server_info is None.
                                If the given peer_server_info is from a server
                                that is not a member of Group Replication.
    :raise GadgetCnxInfoError:  If the connection information on server_info
                                or peer_server_info could not be parsed.
    :raise GadgetServerError:   If a connection fails.

    :return: True if the start_gr_plugin method was executed otherwise False.
    :rtype: boolean
    """

    verbose = kwargs.get("verbose", False)
    dry_run = kwargs.get("dry_run", False)
    gr_host = kwargs.get("gr_address", None)
    option_file = kwargs.get("option_file", None)
    skip_backup = kwargs.get("skip_backup", False)
    # Default is value for ssl_mode is REQUIRED
    ssl_mode = kwargs.get("ssl_mode", GR_SSL_REQUIRED)
    skip_rpl_user = kwargs.get("skip_rpl_user", False)
    target_is_local = kwargs.get("target_is_local", False)
    exit_state_action = kwargs.get("exit_state_action", None)
    member_weight = kwargs.get("member_weight", None)
    failover_consistency = kwargs.get("failover_consistency", None)
    expel_timeout = kwargs.get("expel_timeout", None)

    # Connect to the server
    server = get_server(server_info=server_info)
    if server is None:
        raise GadgetError(_ERROR_NO_SERVER)

    msg = _RUNNING_COMMAND.format(JOIN, server)
    _LOGGER.info("")
    _LOGGER.step(msg)

    _LOGGER.step("Checking Group Replication "
                                      "prerequisites.")

    peer_server = get_server(server_info=peer_server_info)

    # Connect to the peer server
    if peer_server is None:
        if server is not None:
            server.disconnect()
        raise GadgetError("No peer server provided. It is required to get "
                          "information from the group.")

    try:
        # verify the peer server belong to a GR group.
        if not is_member_of_group(peer_server):
            raise GadgetError("Peer server '{0}' is not a member of a GR "
                              "group.".format(peer_server))

        # verify the server status is ONLINE
        peer_server_state = get_member_state(peer_server)
        if peer_server_state != 'ONLINE':
            raise GadgetError("Cannot join instance {0}. Peer instance {1} "
                              "state is currently '{2}', but is expected to "
                              "be 'ONLINE'.".format(server, peer_server,
                                                    peer_server_state))

        # Throw an error in case server doesn't support SSL and the ssl_mode
        # option was provided a value other than DISABLED
        if server.select_variable(HAVE_SSL) != 'YES' and \
           ssl_mode != GR_SSL_DISABLED:
            raise GadgetError(_ERROR_NO_HAVE_SSL.format(server))

        # Throw an error in case there is any SSL incompatibilities are found
        # on the peer server or if the peer-server is incompatible with the
        # value of the ssl_mode option.
        check_peer_ssl_compatibility(peer_server, ssl_mode)

        if not skip_rpl_user:
            rpl_user_dict = get_rpl_usr(kwargs)
        else:
            rpl_user_dict = None

        # Do not check/create the replication user in the instance to add,
        # in order to avoid errors if it is in read-only-mode.
        # (GR automatically enables super-read-only when stopping the
        # plugin, starting with version 8.0.2)
        req_dict = get_req_dict(server, None, peer_server,
                                option_file=option_file)

        check_server_requirements(server, req_dict, rpl_user_dict, verbose,
                                  dry_run, skip_backup=skip_backup,
                                  var_change_warning=True)

        # verify the group replication is installed and not disabled.
        check_gr_plugin_is_installed(server, option_file, dry_run)

        # attempt to set the group_replication_exit_state_action in order to
        # let GR do the value validation and catch any error right away
        if exit_state_action is not None:
            validate_exit_state_action(server, exit_state_action, dry_run)

        # attempt to set the group_replication_member_weight in order to
        # let GR do the value validation and catch any error right away
        if member_weight is not None:
            validate_member_weight(server, member_weight, dry_run)

        # attempt to set the group_replication_consistency in order to
        # let GR do the value validation and catch any error right away
        if failover_consistency is not None:
            validate_failover_consistency(server, failover_consistency, dry_run)

        # attempt to set the group_replication_member_expel_timeout in order to
        # let GR do the value validation and catch any error right away
        if expel_timeout is not None:
            validate_expel_timeout(server, expel_timeout, dry_run)

        # Initialize log error access and get current position in it
        error_log_size = None
        # is_alias(127.0.0.1) != is_alias(gethostname()), but they should
        # match. also is_alias() can't be made to work nicely with recording
        if target_is_local: # server.is_alias("127.0.0.1"):
            try:
                error_log = LocalErrorLog(server)
                error_log_size = error_log.get_size()
            except Exception as err:  # pylint: disable=W0703
                _LOGGER.warning(
                    "Unable to access the server error log: %s", str(err))
        else:
            _LOGGER.warning("Not running locally on the server and can not "
                            "access its error log.")

        # verify the server does not belong already to a GR group.
        if is_active_member(server):
            health(server, **kwargs)
            raise GadgetError(_ERROR_ALREADY_A_MEMBER.format(server, JOIN))

        gr_host, local_port = resolve_gr_local_address(gr_host, server.host,
                                                       server.port)

        local_address = "{0}:{1}".format(gr_host, local_port)
        _LOGGER.debug("local_address to use: %s", local_address)

        # Get local_address from the peer server to add to the list of
        # group_seeds.
        peer_local_address = get_gr_local_address_from(peer_server)

        if peer_server.select_variable(
            "group_replication_single_primary_mode") in ('1', 'ON'):
            kwargs["single_primary"] = "ON"
        else:
            kwargs["single_primary"] = "OFF"

        option_parser = req_dict.get(OPTION_PARSER, None)
        gr_config_vars = get_gr_config_vars(local_address, kwargs,
                            option_parser, peer_local_address,
                            server_id=server.select_variable("server_id"))

        # The following code has been commented because the logic to create
        # the replication-user has been moved to the Shell c++ code.
        # The code wasn't removed to serve as knowledge base for the MP
        # refactoring to C++

        # Do several replication user related tasks if the
        # skip-replication-user option was not provided
        #if not skip_rpl_user:
            # The replication user for be check/create on the peer server.
            # NOTE: rpl_user_dict["host"] has the FQDN resolved from the host
            # provided by the user
        #    replication_user = "******".format(
        #        rpl_user_dict["recovery_user"], rpl_user_dict["host"])
        #    rpl_user_dict["replication_user"] = replication_user

            # Check the given replication user exists on peer
        #    req_dict_user = get_req_dict_user_check(peer_server,
        #                                            replication_user)

            # Check and create the given replication user on peer server.
            # NOTE: No other checks will be performed, only the replication
            # user.
        #    check_server_requirements(peer_server, req_dict_user,
        #                              rpl_user_dict, verbose, dry_run,
        #                              skip_schema_checks=True)

        # IF the group name is not set, try to acquire it from a peer server.
        if gr_config_vars[GR_GROUP_NAME] is None:
            _LOGGER.debug("Trying to retrieve group replication name from "
                          "peer server.")
            group_name = get_gr_name_from_peer(peer_server)

            _LOGGER.debug("Retrieved group replication name from peer"
                          " server: %s.", group_name)
            gr_config_vars[GR_GROUP_NAME] = group_name

        # Set the single_primary mode according to the value set on peer
        # server
        if gr_config_vars[GR_SINGLE_PRIMARY_MODE] is None:
            gr_config_vars[GR_SINGLE_PRIMARY_MODE] = \
                get_gr_variable_from_peer(peer_server, GR_SINGLE_PRIMARY_MODE)

        if gr_config_vars[GR_GROUP_NAME] is None:
            raise GadgetError(
                _ERROR_UNABLE_TO_GET.format("Group Replication group name",
                                            peer_server))

        _LOGGER.step(
                    "Joining Group Replication group: %s",
                    gr_config_vars[GR_GROUP_NAME])

        if gr_config_vars[GR_GROUP_SEEDS] is None:
            raise GadgetError(
                _ERROR_UNABLE_TO_GET.format("peer addresses", peer_server))

        # Remove IP whitelist variable if not set (by user or from the option
        # file) to use the default server value and not set it with None.
        if gr_config_vars[GR_IP_WHITELIST] is None:
            gr_config_vars.pop(GR_IP_WHITELIST)

        if gr_config_vars[GR_EXIT_STATE_ACTION] is None:
            gr_config_vars.pop(GR_EXIT_STATE_ACTION)

        if gr_config_vars[GR_MEMBER_WEIGHT] is None:
            gr_config_vars.pop(GR_MEMBER_WEIGHT)

        if gr_config_vars[GR_FAILOVER_CONSISTENCY] is None:
            gr_config_vars.pop(GR_FAILOVER_CONSISTENCY)

        if gr_config_vars[GR_EXPEL_TIMEOUT] is None:
            gr_config_vars.pop(GR_EXPEL_TIMEOUT)

        gr_config_vars[GR_START_ON_BOOT] = "ON"

        setup_gr_config(server, gr_config_vars, dry_run=dry_run)

        if not skip_rpl_user:
            # if the skip replication user option was not specified,
            # run the change master to store MySQL replication user name or
            # password information in the master info repository
            do_change_master(server, rpl_user_dict, dry_run=dry_run)

        _LOGGER.step(
                    "Attempting to join to Group Replication group...")

        try:
            start_gr_plugin(server, dry_run)
            _LOGGER.step("Server %s joined "
                        "Group Replication group %s.", server,
                        gr_config_vars[GR_GROUP_NAME])

        except:
            _LOGGER.error("\nGroup Replication join failed.")
            if error_log_size is not None:
                log_data = error_log.read(error_log_size)
                if log_data:
                    _LOGGER.error("Group Replication plugin failed to start. "
                                  "Server error log contains the following "
                                  "errors: \n %s", log_data)
            raise

        # Update the Group Replication options on defaults file.
        # Note: Set group_replication_start_on_boot=ON
        if OPTION_PARSER in req_dict.keys():
            persist_gr_config(req_dict[OPTION_PARSER], gr_config_vars,
                              dry_run=dry_run, skip_backup=skip_backup)

        if dry_run:
            _LOGGER.warning(_WARN_DRY_RUN_USED)
            return False

    finally:
        # disconnect servers.
        if server is not None:
            server.disconnect()
        if peer_server is not None:
            peer_server.disconnect()

    return True
예제 #6
0
def start(server_info, **kwargs):
    """Start a group replication group with the given server information.

    :param server_info: Connection information
    :type  server_info: dict | Server | str
    :param kwargs:      Keyword arguments:
                        gr_address: The host:port that the gcs plugin uses to
                                    other servers communicate with this server.
                        dry_run:    Do not make changes to the server
                        verbose:    If the command should show more verbose
                                    information.
                        option_file: The path to a options file to check
                                     configuration.
                        skip_backup: if True, skip the creation of a backup
                                     file when modifying the options file.
                        skip_schema_checks: Skip schema validation.
                        ssl_mode: SSL mode to be used with group replication.
                        exit_state_action: Group Replication Exit State Action,
                                           must be a string containing either
                                           "ABORT_SERVER", "READ_ONLY", "0"
                                           or "1".
                                           The string is case-insensitive.
                        member_weight: Group Replication Member Weight,
                                       must be an integer value, with a
                                       percentage weight for automatic
                                       primary election on failover.
                        failover_consistency: Group Replication failover
                                              Consistency, must be a string
                                              containing either
                                              "BEFORE_ON_PRIMARY_FAILOVER",
                                              "EVENTUAL", "0" or "1".
                                           The string is case-insensitive.
                        expel_timeout: Group Replication member expel Timeout.
                                       Must must be an integer value
                                       containing the time in seconds to wait
                                       before ghe killer node expels members
                                       suspected of having failed from the
                                       group.
    :type kwargs:       dict

    :raise GadgetError:         If server_info is None.
    :raise GadgetCnxInfoError:  If the connection information on
                                server_info could not be parsed.
    :raise GadgetServerError:   If a connection fails.

    :return: True if the start_gr_plugin method was executed otherwise False.
    :rtype: boolean
    """

    server = get_server(server_info=server_info)
    if server is None:
        raise GadgetError(_ERROR_NO_SERVER)
    msg = _RUNNING_COMMAND.format(START, server)
    _LOGGER.info("")
    _LOGGER.step(msg)

    verbose = kwargs.get("verbose", False)
    dry_run = kwargs.get("dry_run", False)
    gr_host = kwargs.get("gr_address", None)
    skip_schema_checks = kwargs.get("skip_schema_checks", [])
    option_file = kwargs.get("option_file", None)
    skip_backup = kwargs.get("skip_backup", False)
    skip_rpl_user = kwargs.get("skip_rpl_user", False)
    exit_state_action = kwargs.get("exit_state_action", None)
    member_weight = kwargs.get("member_weight", None)
    failover_consistency = kwargs.get("failover_consistency", None)
    expel_timeout = kwargs.get("expel_timeout", None)

    _LOGGER.step("Checking Group Replication prerequisites.")
    try:
        # Throw an error in case server doesn't support SSL and the ssl_mode
        # option was provided a value other than DISABLED
        if server.select_variable(HAVE_SSL) != 'YES' and \
                kwargs["ssl_mode"] != GR_SSL_DISABLED:
            raise GadgetError(_ERROR_NO_HAVE_SSL.format(server))

        # Skip replication user checks if requested.
        rpl_user_dict = None
        if not skip_rpl_user:
            rpl_user_dict = get_rpl_usr(kwargs)

        # Do not check the replication user, already handled by the AdminAPI.
        req_dict = get_req_dict(server, None, option_file=option_file)

        check_server_requirements(server, req_dict, rpl_user_dict, verbose,
                                  dry_run, skip_schema_checks,
                                  skip_backup=skip_backup,
                                  var_change_warning=True)

        gr_host, local_port = resolve_gr_local_address(gr_host, server.host,
                                                       server.port)

        # verify the group replication is not Disabled in the server.
        check_gr_plugin_is_installed(server, option_file, dry_run)

        # attempt to set the group_replication_exit_state_action in order to
        # let GR do the value validation and catch any error right away
        if exit_state_action is not None:
            validate_exit_state_action(server, exit_state_action, dry_run)

        # attempt to set the group_replication_member_weight in order to
        # let GR do the value validation and catch any error right away
        if member_weight is not None:
            validate_member_weight(server, member_weight, dry_run)

        # attempt to set the group_replication_consistency in order to
        # let GR do the value validation and catch any error right away
        if failover_consistency is not None:
            validate_failover_consistency(server, failover_consistency, dry_run)

        # attempt to set the group_replication_member_expel_timeout in order to
        # let GR do the value validation and catch any error right away
        if expel_timeout is not None:
            validate_expel_timeout(server, expel_timeout, dry_run)

        # verify the server does not belong already to a GR group.
        if is_active_member(server):
            health(server, **kwargs)
            raise GadgetError(_ERROR_ALREADY_A_MEMBER.format(server, START))

        local_address = "{0}:{1}".format(gr_host, local_port)

        option_parser = req_dict.get(OPTION_PARSER, None)
        gr_config_vars = get_gr_config_vars(local_address, kwargs,
                option_parser,
                server_id=server.select_variable("server_id"))

        if gr_config_vars[GR_GROUP_SEEDS] is None:
            gr_config_vars.pop(GR_GROUP_SEEDS)

        # Remove IP whitelist variable if not set (by user or from the option
        # file) to use the default server value and not set it with None.
        if gr_config_vars[GR_IP_WHITELIST] is None:
            gr_config_vars.pop(GR_IP_WHITELIST)

        if gr_config_vars[GR_EXIT_STATE_ACTION] is None:
            gr_config_vars.pop(GR_EXIT_STATE_ACTION)

        if gr_config_vars[GR_MEMBER_WEIGHT] is None:
            gr_config_vars.pop(GR_MEMBER_WEIGHT)

        if gr_config_vars[GR_FAILOVER_CONSISTENCY] is None:
            gr_config_vars.pop(GR_FAILOVER_CONSISTENCY)

        if gr_config_vars[GR_EXPEL_TIMEOUT] is None:
            gr_config_vars.pop(GR_EXPEL_TIMEOUT)

        if gr_config_vars[GR_GROUP_NAME] is None:
            new_uuid = get_group_uuid_name(server)
            _LOGGER.debug("A new UUID has been generated for the group "
                          "replication name %s", new_uuid)
            gr_config_vars[GR_GROUP_NAME] = new_uuid

        # Set the single_primary mode, if no value given then set ON as
        # default.
        if gr_config_vars[GR_SINGLE_PRIMARY_MODE] is None:
            gr_config_vars[GR_SINGLE_PRIMARY_MODE] = '"ON"'

        _LOGGER.step("Group Replication group name: %s",
                     gr_config_vars[GR_GROUP_NAME])

        gr_config_vars[GR_START_ON_BOOT] = "ON"

        setup_gr_config(server, gr_config_vars, dry_run=dry_run)

        # Run the change master to store MySQL replication user name or
        # password information in the master info repository, but only if
        # replication user is NOT skipped.
        if not skip_rpl_user:
            do_change_master(server, rpl_user_dict, dry_run=dry_run)

        set_bootstrap(server, dry_run)

        _LOGGER.step("Attempting to start the Group Replication group...")

        # Attempt to start the Group Replication plugin
        start_gr_plugin(server, dry_run)

        # Wait for the super_read_only to be unset.
        super_read_only = server.select_variable("super_read_only", 'global')
        _LOGGER.debug("super_read_only: %s", super_read_only)
        waiting_time = 0
        informed = False
        while int(super_read_only) and waiting_time < TIME_OUT:
            time.sleep(WAIT_SECONDS)
            waiting_time += WAIT_SECONDS
            _LOGGER.debug("have been waiting %s seconds", waiting_time)
            # inform what are we waiting for
            if waiting_time >= 10 and not informed:
                _LOGGER.info("Waiting for super_read_only to be unset.")
                informed = True

            super_read_only = server.select_variable("super_read_only",
                                                     'global')
            _LOGGER.debug("super_read_only: %s", super_read_only)

        if int(super_read_only):
            raise GadgetError("Timeout waiting for super_read_only to be "
                              "unset after call to start Group Replication "
                              "plugin.")

        _LOGGER.step(
                    "Group Replication started for group: %s.",
                    gr_config_vars[GR_GROUP_NAME])

        unset_bootstrap(server, dry_run)

        # Update the Group Replication options on defaults file.
        # Note: Set group_replication_start_on_boot=ON
        if OPTION_PARSER in req_dict.keys():
            persist_gr_config(req_dict[OPTION_PARSER], gr_config_vars,
                              dry_run=dry_run, skip_backup=skip_backup)

        if dry_run:
            _LOGGER.warning(_WARN_DRY_RUN_USED)
            return False

    finally:
        # Disconnect the server prior to end method invocation.
        if server is not None:
            server.disconnect()

    return True