def setUp(self):
     """ Setup server connection
     """
     self.server_cnx = {'conn_info': self.options[SERVER_CNX_OPT][0]}
     self.server = Server(self.server_cnx)
     self.server.connect()
     req_dict = get_req_dict(self.server, None, None, None)
     try:
         check_server_requirements(self.server, req_dict, None,
                                   verbose=False, dry_run=False,
                                   skip_schema_checks=False, update=False)
     except:
         raise unittest.SkipTest("Provided server must fulfill the GR "
                                 "plugin requirements.")
Exemple #2
0
def skip_if_not_GR_approved(server):
    """Verify GR basic requirements on the given server.

    :@param param: server to verify.
    :type param: Server instance.

    :raise SkipTest: if the server does not fulfill the requirements for GR.
    """
    try:
        req_dict = get_req_dict(server, None, None, None)
        check_server_requirements(server, req_dict, None,
                                  verbose=False, dry_run=False,
                                  skip_schema_checks=False, update=False,
                                  skip_backup=True)
    except:
        raise unittest.SkipTest("Provided server must fulfill the GR "
                                "plugin requirements.")
def skip_if_not_GR_approved(server):
    """Verify GR basic requirements on the given server.

    :@param param: server to verify.
    :type param: Server instance.

    :raise SkipTest: if the server does not fulfill the requirements for GR.
    """
    try:
        req_dict = get_req_dict(server, None, None, None)
        check_server_requirements(server,
                                  req_dict,
                                  None,
                                  verbose=False,
                                  dry_run=False,
                                  skip_schema_checks=False,
                                  update=False,
                                  skip_backup=True)
    except:
        raise unittest.SkipTest("Provided server must fulfill the GR "
                                "plugin requirements.")
    def test_check_server_requirements(self):
        """Test check_server_requirements method
        """
        rpl_settings = {
            "recovery_user": "******",
            'rep_user_passwd': 'my_password',
            'replication_user': "******",
            "host": '%',
        }
        req_dict = get_req_dict(self.server, rpl_settings["replication_user"])
        verbose = False
        dry_run = True
        check_server_requirements(self.server, req_dict, rpl_settings, verbose,
                                  dry_run)

        rpl_settings = {
            "recovery_user": "******",
            'rep_user_passwd': 'my_password',
            'replication_user': "******",
            "host": '%',
        }
        req_dict = get_req_dict(self.server, rpl_settings["replication_user"])
        dry_run = True
        check_server_requirements(self.server, req_dict, rpl_settings, verbose,
                                  dry_run)

        check_server_requirements(self.server, req_dict, rpl_settings)
Exemple #5
0
    def test_check_server_requirements_config_vars(self):
        """Tests check_server_requirements method"""

        # The dict object with the requirements to check.
        req_dict = {}
        # Add the variables to check on server.
        req_dict[SERVER_VARIABLES] = {
            'session_track_system_variables': {
                ONE_OF: ("", )
            },
            "log_bin": {
                ONE_OF: ("1", "ON")
            },
        }
        self.server1.exec_query(
            "set @@global.session_track_system_variables='time_zone'")

        # Allow the variable to change dynamically.
        dynamic_vars = set(DYNAMIC_SERVER_VARS)
        dynamic_vars.add('session_track_system_variables')

        self.assertTrue(
            check_server_requirements(self.server1,
                                      req_dict,
                                      None,
                                      dry_run=False,
                                      skip_backup=True,
                                      update=True,
                                      dynamic_vars=dynamic_vars))

        self.assertEqual(
            self.server1.select_variable('session_track_system_variables',
                                         "global"), "")

        # Change the variables value.
        req_dict[SERVER_VARIABLES]['session_track_system_variables'] = {
            ONE_OF: ("", )
        }

        # check_server_requirements updates by default
        self.assertTrue(
            check_server_requirements(self.server1,
                                      req_dict,
                                      None,
                                      verbose=True,
                                      dry_run=False,
                                      skip_backup=True,
                                      dynamic_vars=dynamic_vars))

        self.assertEqual(
            self.server1.select_variable('session_track_system_variables',
                                         "global"), "")

        work_file = os.path.join(self.option_file_dir, "my_test.cnf")
        orig_file = os.path.join(self.option_file_dir, "my.cnf")

        # test requirements from a configuration file.
        shutil.copy(orig_file, work_file)

        req_dict[OPTION_PARSER] = MySQLOptionsParser(work_file)
        req_dict[CONFIG_SETTINGS] = {
            'session_track_system_variables': {
                ONE_OF: ("autocommit", )
            },
            "log_bin": {
                NOT_IN: ("ON", "1", "<not set>"),
                DEFAULT: "0"
            },
        }

        try:
            opt_file_parser = MySQLOptionsParser(work_file)
            # make sure section mysqld exist.
            self.assertTrue(opt_file_parser.has_section('mysqld'))

            if opt_file_parser.has_option('mysqld', 'log_bin'):
                # delete it.
                opt_file_parser.remove_option('mysqld', 'log_bin')

            self.assertFalse(opt_file_parser.has_option('mysqld', 'log_bin'))

            # write the changes
            opt_file_parser.write()

            self.assertTrue(
                check_server_requirements(self.server1,
                                          req_dict,
                                          None,
                                          verbose=True,
                                          dry_run=False,
                                          skip_backup=True,
                                          dynamic_vars=dynamic_vars))

            # check_server_requirements should change the value of
            # session_track_system_variables and log_bin
            opt_file_parser = MySQLOptionsParser(work_file)
            self.assertTrue(opt_file_parser.has_option('mysqld', 'log_bin'))
            self.assertEqual(opt_file_parser.get('mysqld', 'log_bin'), "0")
            self.assertTrue(
                opt_file_parser.has_option('mysqld',
                                           'session_track_system_variables'))
            self.assertEqual(
                opt_file_parser.get('mysqld',
                                    'session_track_system_variables'),
                "autocommit")

            req_dict[OPTION_PARSER] = MySQLOptionsParser(work_file)
            req_dict[CONFIG_SETTINGS] = {
                'session_track_system_variables': {
                    ONE_OF: ("<no value>", )
                },
                "log_bin": {
                    NOT_IN: ("OFF", "0", "<not set>"),
                    DEFAULT: "<no value>"
                },
            }

            self.assertTrue(
                check_server_requirements(self.server1,
                                          req_dict,
                                          None,
                                          verbose=True,
                                          dry_run=False,
                                          skip_backup=True,
                                          dynamic_vars=dynamic_vars))

            # check_server_requirements should change the value of
            # session_track_system_variables and log_bin
            opt_file_parser = MySQLOptionsParser(work_file)
            self.assertTrue(opt_file_parser.has_option('mysqld', 'log_bin'))
            self.assertEqual(opt_file_parser.get('mysqld', 'log_bin'), None)
            self.assertTrue(
                opt_file_parser.has_option('mysqld',
                                           'session_track_system_variables'))
            self.assertEqual(
                opt_file_parser.get('mysqld',
                                    'session_track_system_variables'), None)

        finally:
            try:
                os.remove(work_file)
            except OSError:
                pass
Exemple #6
0
    def test_check_server_requirements(self):
        """Tests check_server_requirements method"""
        skip_if_not_GR_approved(self.server1)
        self.server.exec_query("drop user if exists 'new_rpl_user'")
        self.server.exec_query("drop user if exists 'replic_user'@'%'")
        self.server.exec_query("drop user if exists 'replic_user'@'localhost'")
        # Test with default values, server.user must have SUPER
        options = {
            "replication_user": "******",
            "rep_user_passwd": "rplr_pass",
        }
        rpl_settings = get_rpl_usr(options)
        req_dict = get_req_dict(self.server, rpl_settings["replication_user"])
        self.assertTrue(
            check_server_requirements(self.server,
                                      req_dict,
                                      rpl_settings,
                                      verbose=True,
                                      dry_run=False,
                                      skip_backup=True))

        self.assertTrue(
            User(self.server, "replic_user@%", None).exists(),
            "User was not created on check_server_requirements")

        # Test using an admin user without CREATE USER privilege.
        grant_list = ["SELECT"]
        self.server.exec_query("DROP USER IF EXISTS "
                               "'replic_user'@'localhost'")
        change_user_privileges(self.server,
                               "replic_user",
                               'localhost',
                               "rplr_pass",
                               grant_list=grant_list,
                               create_user=True)

        server2 = Server({
            "conn_info":
            "replic_user@localhost:{0}"
            "".format(self.server.port)
        })
        server2.passwd = "rplr_pass"
        server2.connect()
        qry_key = ("select MEMBER_HOST, MEMBER_PORT from {0}"
                   "".format(REP_GROUP_MEMBERS_TABLE))

        frozen_queries = {qry_key: [[server2.host, server2.port]]}
        mock_server = get_mock_server(server2, variables=frozen_queries)

        options = {
            "replication_user": "******",
            "rep_user_passwd": "rpl_pass",
        }
        rpl_settings = get_rpl_usr(options)
        req_dict = get_req_dict(server2, rpl_settings["replication_user"])

        # expect GadgetError: Query failed: No required privileges
        #                                   to create the replication user
        with self.assertRaises(GadgetError) as test_raises:
            check_server_requirements(mock_server,
                                      req_dict,
                                      rpl_settings,
                                      verbose=True,
                                      dry_run=False,
                                      skip_backup=True)
        exception = test_raises.exception
        self.assertTrue(
            "required privileges to create" in exception.errmsg,
            "The exception message was not the expected. {0}"
            "".format(exception.errmsg))

        # Test existing user and admin user without REPLICATION SLAVE grant
        grant_list = ["SELECT"]
        revoke_list = ["REPLICATION SLAVE"]
        change_user_privileges(self.server,
                               "new_rpl_user",
                               "%",
                               grant_list=grant_list,
                               revoke_list=revoke_list,
                               create_user=True,
                               with_grant=True)
        revoke_list = ["REPLICATION SLAVE"]
        change_user_privileges(self.server,
                               "replic_user",
                               "%",
                               revoke_list=revoke_list)

        # expect GadgetError: does not have the REPLICATION SLAVE privilege,
        #                     and can not be granted by.
        with self.assertRaises(GadgetError) as test_raises:
            check_server_requirements(mock_server,
                                      req_dict,
                                      rpl_settings,
                                      verbose=True,
                                      dry_run=False,
                                      skip_backup=True)
        exception = test_raises.exception
        self.assertTrue(
            "does not have the REPLICATION SLAVE privilege, "
            "and can not be granted by" in exception.errmsg,
            "The exception message was not the expected. {0}"
            "".format(exception.errmsg))

        self.assertTrue(
            "SUPER privilege required to disable the binlog."
            in exception.errmsg,
            "The exception message was not the expected. {0}"
            "".format(exception.errmsg))

        # self.server.exec_query("drop user if exists 'replic_user'@'%'")
        # Test existing user and admin user without REPLICATION SLAVE grant
        grant_list = ["SELECT"]
        revoke_list = ["REPLICATION SLAVE"]
        change_user_privileges(self.server,
                               "new_rpl_user",
                               "%",
                               grant_list=grant_list,
                               revoke_list=revoke_list)
        grant_list = ["REPLICATION SLAVE", "SUPER"]
        change_user_privileges(self.server,
                               "replic_user",
                               "localhost",
                               grant_list=grant_list,
                               with_grant=True)

        # reset session to get new privileges.
        server2.disconnect()
        server2.connect()
        mock_server = get_mock_server(server2, variables=frozen_queries)

        self.assertTrue(
            check_server_requirements(mock_server,
                                      req_dict,
                                      rpl_settings,
                                      verbose=True,
                                      dry_run=False,
                                      skip_backup=True))

        # Test existing rpl user and admin user without grant
        # admin user: replic_user
        # rpl user: new_rpl_user
        grant_list = ["REPLICATION SLAVE"]
        change_user_privileges(self.server,
                               "new_rpl_user",
                               "%",
                               grant_list=grant_list)
        grant_list = ["SELECT", "CREATE USER"]
        change_user_privileges(self.server,
                               "create_rpl",
                               "127.0.0.1",
                               user_passwd="c_pass",
                               grant_list=grant_list,
                               create_user=True,
                               with_grant=True)
        server3 = Server({
            "conn_info":
            "[email protected]:{0}"
            "".format(self.server.port)
        })
        server3.passwd = "c_pass"
        server3.connect()
        req_dict3 = get_req_dict(server3, rpl_settings["replication_user"])
        # expect GadgetError: No required privileges
        #                     to grant Replication Slave privilege
        with self.assertRaises(GadgetError) as test_raises:
            check_server_requirements(server3,
                                      req_dict3,
                                      rpl_settings,
                                      verbose=True,
                                      dry_run=False,
                                      skip_backup=True)
        exception = test_raises.exception
        self.assertTrue(
            "SUPER privilege needed to run the CHANGE MASTER "
            "command" in exception.errmsg,
            "The exception message was not the expected. {0}"
            "".format(exception.errmsg))

        # Test invalid server_id
        mock_server = get_mock_server(self.server,
                                      variables={"server_id": "0"})
        req_dict = get_req_dict(self.server, rpl_settings["replication_user"])

        # expect GadgetError: server_id not valid
        with self.assertRaises(GadgetError) as test_raises:
            check_server_requirements(mock_server,
                                      req_dict,
                                      rpl_settings,
                                      verbose=True,
                                      dry_run=False,
                                      skip_backup=True)
        exception = test_raises.exception
        self.assertTrue(
            "is not valid, it must be a positive integer" in exception.errmsg,
            "The exception message was not the expected. {0}"
            "".format(exception.errmsg))

        # Test duplicate server_id
        mock_server = get_mock_server(self.server,
                                      variables={"server_id": "101"})
        req_dict["SERVER_ID"] = {"peers": [mock_server]}
        # expect GadgetError: server_id is already used by peer
        with self.assertRaises(GadgetError) as test_raises:
            check_server_requirements(mock_server,
                                      req_dict,
                                      rpl_settings,
                                      verbose=True,
                                      dry_run=False,
                                      skip_backup=True)
        exception = test_raises.exception
        self.assertTrue(
            "is already used by peer" in exception.errmsg,
            "The exception message was not the expected. {0}"
            "".format(exception.errmsg))

        # Test existing user and admin with required grants
        req_dict = get_req_dict(self.server, rpl_settings["replication_user"])
        grant_list = ["REPLICATION SLAVE"]
        change_user_privileges(self.server,
                               "new_rpl_user",
                               "%",
                               grant_list=grant_list)
        self.assertTrue(
            check_server_requirements(self.server,
                                      req_dict,
                                      rpl_settings,
                                      verbose=True,
                                      dry_run=False,
                                      skip_backup=True))

        # Tests server variables not meet required values
        req_dict = get_req_dict(self.server, rpl_settings["replication_user"])
        req_dict[SERVER_VARIABLES] = {
            "log_bin": {
                ONE_OF: ("0", )
            },
            "binlog_format": {
                ONE_OF: ("", )
            },
            "binlog_checksum": {
                ONE_OF: ("", )
            },
            "gtid_mode": {
                ONE_OF: ("OFF", )
            },
            "log_slave_updates": {
                ONE_OF: ("0", )
            },
            "enforce_gtid_consistency": {
                ONE_OF: ("OFF", )
            },
        }
        # expect GadgetError: change the configuration values
        with self.assertRaises(GadgetError) as test_raises:
            check_server_requirements(self.server,
                                      req_dict,
                                      rpl_settings,
                                      verbose=True,
                                      dry_run=True,
                                      update=False,
                                      skip_backup=True)
        exception = test_raises.exception
        self.assertIn(
            "on server {0} are incompatible with Group "
            "Replication.".format(self.server), exception.errmsg,
            "The exception message was not the expected. {0}"
            "".format(exception.errmsg))

        # Test server version
        req_dict[SERVER_VERSION] = "99.9.9"
        # expect GadgetError: Query failed: server version
        with self.assertRaises(GadgetError) as test_raises:
            check_server_requirements(self.server,
                                      req_dict,
                                      rpl_settings,
                                      verbose=True,
                                      dry_run=True,
                                      update=False,
                                      skip_backup=True)
        exception = test_raises.exception
        self.assertTrue(
            "does not meet the required MySQL server version"
            in exception.errmsg,
            "The exception message was not the expected. {0}"
            "".format(exception.errmsg))

        self.server.exec_query("drop user if exists 'replic_user'")
        self.server.exec_query("drop user if exists 'create_rpl'")
        self.server.exec_query("drop user if exists 'new_rpl_user'")
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
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
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
Exemple #10
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
Exemple #11
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