Exemplo n.º 1
0
class TestGRAdmin(GadgetsTestCase):
    """This class tests the methods in mysql_gadgets.command.gr_admin
    """
    @property
    def num_servers_required(self):
        """Property defining the number of servers required by the test.
        """
        return 1

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

        skip_if_not_GR_approved(self.server)

        if self.server.select_variable(HAVE_SSL) != 'YES':
            raise unittest.SkipTest("Provided server must have_ssl == 'YES'.")

        # User without Create User privileges
        self.server.exec_query("drop user if exists 'nop_user'@'localhost'")
        self.server.exec_query("create user nop_user@'localhost'")
        server_conn2 = self.server_cnx.copy()
        server_conn2["conn_info"] = ("nop_user@localhost:{0}"
                                     "".format(self.server.port))
        self.server2 = Server(self.server_cnx)
        self.server2.connect()

        columns = [MEMBER_ID, MEMBER_HOST, MEMBER_PORT, MEMBER_STATE]
        qry_key = "SELECT {0} FROM {1}".format(", ".join(columns),
                                               REP_GROUP_MEMBERS_TABLE)
        qry_key2 = ("SELECT GROUP_NAME FROM {0} where "
                    "CHANNEL_NAME = 'group_replication_applier'"
                    "".format(REP_CONN_STATUS_TABLE))
        frozen_queries = {
            qry_key: [[], []],
            qry_key2: [],
        }
        self.mock_no_member = get_mock_server(self.server,
                                              queries=frozen_queries)

    def tearDown(self):
        """ Disconnect base server (for all tests).
        """
        # reconnect the server.
        self.server.connect()

        leave(self.server)

        # reconnect the server.
        self.server.connect()

        self.server.exec_query("drop user if exists 'rpl_user'")
        self.server.exec_query("drop user if exists 'nop_user'@'localhost'")
        self.server.exec_query("drop user if exists 'new_rpl_user'")
        self.server.disconnect()
        self.server2.disconnect()

    def test_bootstrap(self):
        """Tests start method
        """
        # Fill the options
        options = {
            "group_name": None,
            "replication_user": None,
            "rep_user_passwd": "passwd",
            "verbose": True
        }

        # test start
        if is_member_of_group(self.server):
            leave(self.server, **options)

        # reconnect the server.
        self.server.connect()

        self.assertTrue(start(self.server, **options))

        # reconnect the server.
        self.server.connect()

        # health
        health(self.server, **options)

        # reconnect the server.
        self.server.connect()

        # Bootstrap active server
        # Trying to start a group with a server that is already a member
        # expect GadgetError: Query failed: is already a member of a GR group.
        with self.assertRaises(GadgetError) as test_raises:
            start(self.server, **options)
        exception = test_raises.exception
        self.assertTrue(
            "is already a member" in exception.errmsg,
            "The exception message was not the expected. {0}"
            "".format(exception.errmsg))

        # reconnect the server.
        self.server.connect()

        leave(self.server, **options)

        # reconnect the server.
        self.server.connect()

        group_name = "b7286041-3016-11e6-ba52-507b9d87510a"
        # start using not defaults
        options = {
            "group_name": "b7286041-3016-11e6-ba52-507b9d87510a",
            "gr_host": "{0}:".format(self.server.host),
            "replication_user": "******",
            "rep_user_passwd": "passwd",
            "verbose": True,
            "dry_run": False,
        }

        self.assertTrue(start(self.server, **options))
        # reconnect the server.
        self.server.connect()
        self.assertTrue(is_member_of_group(self.server, group_name))

        self.assertFalse(is_member_of_group(self.server, "group_name"))

        if is_member_of_group(self.server):
            leave(self.server, **options)

        # reconnect the server.
        self.server.connect()

    def test_bootstrap_noselect_priv(self):
        """Tests commands without Select privilege.
        """
        self.server.exec_query("drop user if exists 'not_select'@'%'")
        change_user_privileges(self.server,
                               "not_select",
                               "%",
                               user_passwd="pass",
                               create_user=True)

        server1 = Server(
            {"conn_info": "not_select@localhost:{0}".format(self.server.port)})
        server1.passwd = "pass"
        server1.connect()
        # Fill the options
        options = {
            "group_name": "b7286041-3016-11e6-ba52-507b9d87510a",
            "gr_host": ":{0}".format(self.server.port),
            "replication_user": "******",
            "rep_user_passwd": "passwd",
            "verbose": True,
            "dry_run": False,
        }

        # expect GadgetError: Query failed: not have enough privileges to
        #                                   verify server
        with self.assertRaises(GadgetError) as test_raises:
            leave(server1, **options)
        exception = test_raises.exception
        self.assertTrue(
            "not have enough privileges to verify server" in exception.errmsg,
            "The exception message was not the expected. {0}"
            "".format(exception.errmsg))

        # reconnect the server.
        server1.connect()

        # expect GadgetError: Query failed: not have enough privileges to
        #                                   verify server
        with self.assertRaises(GadgetError) as test_raises:
            start(server1, **options)
        exception = test_raises.exception
        self.assertTrue(
            "The operation could not continue due to" in exception.errmsg,
            "The exception message was not the expected. {0}"
            "".format(exception.errmsg))

        # reconnect the server.
        server1.connect()

        # expect GadgetError: Query failed: not have enough privileges to
        #                                   verify server
        with self.assertRaises(GadgetError) as test_raises:
            health(server1, **options)
        exception = test_raises.exception
        self.assertTrue(
            "not have enough privileges to verify server" in exception.errmsg,
            "The exception message was not the expected. {0}"
            "".format(exception.errmsg))

        # reconnect the server.
        server1.connect()

        # expect GadgetError: Query failed: not have enough privileges to
        #                                   verify server
        with self.assertRaises(GadgetError) as test_raises:
            leave(server1, **options)
        exception = test_raises.exception
        self.assertTrue(
            "not have enough privileges to verify server" in exception.errmsg,
            "The exception message was not the expected. {0}"
            "".format(exception.errmsg))

    def test_re_bootstrap(self):
        """Tests start method over actively replicating server
        """
        # Fill the options
        options = {
            "group_name": None,
            "replication_user": None,
            "rep_user_passwd": "passwd",
            "gr_host": "",
        }

        # test start
        if is_member_of_group(self.server):
            leave(self.server, **options)

        # reconnect the server.
        self.server.connect()

        self.assertTrue(start(self.server, **options))

        # reconnect the server.
        self.server.connect()

        # test health
        health(self.server, **options)

        # reconnect the server.
        self.server.connect()

        # Trying to start a group with a server that is already a member
        # expect GadgetError: Query failed: is already a member of a GR group.
        with self.assertRaises(GadgetError) as test_raises:
            start(self.server, **options)
        exception = test_raises.exception
        self.assertTrue(
            "is already a member" in exception.errmsg,
            "The exception message was not the expected. {0}"
            "".format(exception.errmsg))

        # reconnect the server.
        self.server.connect()

        # Bootstrap with dry-run
        options["dry_run"] = True
        # Trying to start a group with a server that is already a member
        # expect GadgetError: Query failed: is already a member of a GR group.
        with self.assertRaises(GadgetError) as test_raises:
            start(self.server, **options)
        exception = test_raises.exception
        self.assertTrue(
            "is already a member" in exception.errmsg,
            "The exception message was not the expected. {0}"
            "".format(exception.errmsg))

        # reconnect the server.
        self.server.connect()

        self.assertTrue(leave(self.server, **options))

        # reconnect the server.
        self.server.connect()

    def test_join(self):
        """Tests join method
        """
        # Fill the options
        options = {
            "group_name": None,
            "gr_host": "{0}:{1}".format(self.server.host, self.server.port),
            "replication_user": "******",
            "rep_user_passwd": "passwd",
            "verbose": False,
            "dry_run": False,
        }

        # join needs start
        if is_member_of_group(self.server):
            leave(self.server, **options)

        # reconnect the server.
        self.server.connect()

        self.assertTrue(start(self.server, **options))

        # reconnect the server.
        self.server.connect()

        # health
        health(self.server, **options)

        # reconnect the server.
        self.server.connect()

        leave(self.server, **options)

        # reconnect the server.
        self.server.connect()

        member_state_qry = ("SELECT MEMBER_STATE FROM {0} as m JOIN {1} "
                            "as s on m.MEMBER_ID = s.MEMBER_ID"
                            "".format(REP_GROUP_MEMBERS_TABLE,
                                      REP_MEMBER_STATS_TABLE))
        frozen_queries = {
            member_state_qry: [
                [
                    'ONLINE',
                ],
            ],
        }
        mock_server = get_mock_server(self.server, queries=frozen_queries)

        # Join with defaults ("dry_run": True) and required password
        options = {
            "dry_run": True,
            "replication_user": "******",
            "rep_user_passwd": "passwd",
        }
        join(self.server, mock_server, **options)

        # reconnect the server.
        self.server.connect()

        # Join with no defaults ("dry_run": True)
        options = {
            "group_name": None,
            "gr_host": "{0}:{1}".format(self.server.host, self.server.port),
            "replication_user": "******",
            "rep_user_passwd": "passwd",
            "verbose": False,
            "dry_run": True,
        }
        # leave the group
        if is_member_of_group(self.server):
            leave(self.server, **options)

        self.assertFalse(join(self.server, mock_server, **options))

        # reconnect the server.
        self.server.connect()

        self.assertFalse(leave(self.server, **options))

        # reconnect the server.
        self.server.connect()

    def test_re_join(self):
        """Tests join method over actively replicating server
        """
        # Fill the options
        options = {
            "group_name": None,
            "gr_host": self.server.host,
            "replication_user": "******",
            "rep_user_passwd": "passwd",
            "verbose": False,
            "dry_run": False,
        }

        # test start
        leave(self.server, **options)

        # reconnect the server.
        self.server.connect()

        self.assertTrue(start(self.server, **options))

        # reconnect the server.
        self.server.connect()

        # test health
        health(self.server, **options)

        # reconnect the server.
        self.server.connect()

        options["dry_run"] = True
        # test join
        mock_server = get_mock_server(self.server)
        # Trying to add server to a group while is already a member of a group
        # expect GadgetError: Query failed: is already a member of a GR group.
        with self.assertRaises(GadgetError) as test_raises:
            join(self.server, mock_server, **options)
        exception = test_raises.exception
        self.assertTrue(
            "is already a member" in exception.errmsg,
            "The exception message was not the expected. {0}"
            "".format(exception.errmsg))

        # reconnect the server.
        self.server.connect()

        self.assertTrue(leave(self.server, **options))

        # reconnect the server.
        self.server.connect()

    def test_health_with_not_a_member(self):
        """Test the commands that requires a GR member or None value for a
        required server connection information.
        """
        # Fill the options
        options = {}

        # expect GadgetError: Query failed: no server was given
        with self.assertRaises(GadgetError) as test_raises:
            health(None, **options)
        exception = test_raises.exception
        self.assertTrue(
            "No server was given" in exception.errmsg,
            "The exception message was not the expected. {0}"
            "".format(exception.errmsg))

        # expect GadgetError: Query failed: not a member of a Group
        with self.assertRaises(GadgetError) as test_raises:
            health(self.mock_no_member, **options)
        exception = test_raises.exception
        self.assertTrue(
            "not a member of a GR group" in exception.errmsg,
            "The exception message was not the expected. {0}"
            "".format(exception.errmsg))

        # Fill the options
        options = {
            "group_name": None,
            "replication_user": None,
            "rep_user_passwd": "passwd",
        }

        # expect GadgetError: Query failed: No server was given
        with self.assertRaises(GadgetError) as test_raises:
            start(None, **options)
        exception = test_raises.exception
        self.assertTrue(
            "No server was given" in exception.errmsg,
            "The exception message was not the expected: {0}"
            "".format(exception.errmsg))

    def test_join_with_none_values(self):
        """Test the commands that requires a GR member or None value for a
        required server connection information.
        """
        # Fill the options
        options = {
            "group_name": None,
            "replication_user": None,
            "rep_user_passwd": "passwd",
            "gr_host": None,
        }

        # expect GadgetError: Query failed: No peer server was given
        with self.assertRaises(GadgetError) as test_raises:
            join(None, None, **options)
        exception = test_raises.exception
        self.assertTrue(
            "No server was given" in exception.errmsg,
            "The exception message was not the expected: {0}"
            "".format(exception.errmsg))

        # expect GadgetError: Query failed: No peer server was given
        with self.assertRaises(GadgetError) as test_raises:
            join(self.server, None, **options)
        exception = test_raises.exception
        self.assertTrue(
            "No peer server provided" in exception.errmsg,
            "The exception message was not the expected: {0}"
            "".format(exception.errmsg))

        # reconnect the server.
        self.server.connect()

        # expect GadgetError: Query failed: not a member of a Group
        with self.assertRaises(GadgetError) as test_raises:
            join(self.server, self.mock_no_member, **options)
        exception = test_raises.exception
        self.assertTrue(
            "not a member of a GR group" in exception.errmsg,
            "The exception message was not the expected: {0}"
            "".format(exception.errmsg))

        # reconnect the server.
        self.server.connect()

    def test_leave_with_none_values(self):
        """Test the commands that requires a GR member or None value for a
        required server connection information.
        """
        # Fill the options
        options = {}
        # expect GadgetError: Query failed: not a member of a Group
        with self.assertRaises(GadgetError) as test_raises:
            leave(None, **options)
        exception = test_raises.exception
        self.assertTrue(
            "No server was given" in exception.errmsg,
            "The exception message was not the expected: {0}"
            "".format(exception.errmsg))

        # expect GadgetError: Query failed: not a member of a Group
        with self.assertRaises(GadgetError) as test_raises:
            leave(self.mock_no_member, **options)
        exception = test_raises.exception
        self.assertTrue(
            "not a member of a Group" in exception.errmsg,
            "The exception message was not the expected: {0}"
            "".format(exception.errmsg))

    def test_resolve_gr_local_address(self):
        """Tests resolve_gr_local_address method.
        """
        # Use server host and port
        gr_host = ""
        server_host = self.server.host
        server_port = get_free_random_port()
        self.assertEqual(
            resolve_gr_local_address(gr_host, server_host, server_port),
            (self.server.host, repr(server_port + 10000)))

        # Use host from gr_host
        gr_host = "host4321"
        server_port = get_free_random_port()
        self.assertEqual(
            resolve_gr_local_address(gr_host, server_host, server_port),
            ("host4321", repr(server_port + 10000)))

        # Use host from gr_host
        gr_host = "127.0.0.1:"
        server_port = get_free_random_port()
        self.assertEqual(
            resolve_gr_local_address(gr_host, server_host, server_port),
            ("127.0.0.1", repr(server_port + 10000)))

        # Use given IPv6 host and port from gr_host
        gr_host = "[1:2:3:4:5:6:7:8]:1234"
        self.assertEqual(
            resolve_gr_local_address(gr_host, server_host, server_port),
            ("1:2:3:4:5:6:7:8", "1234"))

        # Use given IPv6 host and port from gr_host
        server_port = get_free_random_port()
        gr_host = "E3D7::51F4:9BC8:{0}".format(server_port)
        self.assertEqual(
            resolve_gr_local_address(gr_host, server_host, server_port),
            ("E3D7::51F4:9BC8", repr(server_port)))

        # Use given port from gr_host
        gr_host = "1234"
        server_port = get_free_random_port()
        self.assertEqual(
            resolve_gr_local_address(gr_host, server_host, server_port),
            (self.server.host, "1234"))

        # Given port on gr_host is out of range, generate a new one instead
        gr_host = "65536"
        self.assertIsInstance(
            int(
                resolve_gr_local_address(gr_host, server_host,
                                         server_port)[1]), int)

        # Use given port from gr_host
        gr_host = ":1234"
        self.assertEqual(
            resolve_gr_local_address(gr_host, server_host, server_port),
            (self.server.host, "1234"))

        # Use given IPv6 host from gr_host and generate random port.
        gr_host = "E3D7::51F4:9BC8"
        self.assertEqual(
            resolve_gr_local_address(gr_host, server_host, server_port)[0],
            "E3D7::51F4:9BC8")
        self.assertIsInstance(
            int(
                resolve_gr_local_address(gr_host, server_host,
                                         server_port)[1]), int)
Exemplo n.º 2
0
class TestGroupReplication(GadgetsTestCase):
    """Test class for mysql_gadgets.common.group_replication
    """
    @property
    def num_servers_required(self):
        """Property defining the number of servers required by the test.
        """
        return 1

    def setUp(self):
        """ Setup server connection
        """
        server_cnx = {'conn_info': self.options[SERVER_CNX_OPT][0]}
        self.server1 = Server(server_cnx)
        self.server1.connect()
        # default user
        self.server1.exec_query("drop user if exists 'rpl_user'")
        qry_key1 = ("select MEMBER_HOST, MEMBER_PORT from {0}"
                    "".format(REP_GROUP_MEMBERS_TABLE))
        qry_key2 = "show variables like 'group_replication_%'"
        frozen_queries = {
            qry_key1: [[self.server1.host, self.server1.port]],
            qry_key2: [("group_replication_group_name", "name"),
                       ("group_replication_start_on_boot", "ON"),
                       ("group_replication_group_seeds", "")]
        }
        variables = {GR_LOCAL_ADDRESS: "localhost:3307"}
        self.server = get_mock_server(self.server1,
                                      queries=frozen_queries,
                                      variables=variables)
        # Set directory with option files for tests.
        self.option_file_dir = os.path.normpath(
            os.path.join(__file__, "..", "std_data", "option_files"))

        self.session_track_system_variables_bkp = self.server1.exec_query(
            "select @@global.session_track_system_variables")[0][0]

    def tearDown(self):
        """ Disconnect base server (for all tests).
        """
        queries = [
            "drop user if exists 'rpl_user'",
            "drop user if exists 'replic_user'@'localhost'",
            "drop user if exists 'create_rpl'@'127.0.0.1'",
            "drop user if exists 'replic_user'",
            "set @@global.session_track_system_variables='{0}'".format(
                self.session_track_system_variables_bkp),
        ]
        for query in queries:
            self.server1.exec_query(query)
        self.server1.disconnect()

    def test_get_gr_members(self):
        """Tests get_gr_members method"""

        self.assertIsInstance(
            get_gr_members(self.server)[0], Server,
            "An object of class server was "
            "expected")

    def test__print_check_unique_id_results(self):
        """Tests _print_check_unique_id_results method"""
        server_id_res = {"pass": True}
        _print_check_unique_id_results(server_id_res)

        server_id_res = {"pass": False, "duplicate": None}
        _print_check_unique_id_results(server_id_res)

        server_id_res = {"pass": False, "duplicate": self.server}
        _print_check_unique_id_results(server_id_res)

    def test_print_check_server_version_results(self):
        """Tests _print_check_server_version_results method"""
        server_ver_res = {"pass": False, SERVER_VERSION: [5, 6, 4]}
        self.assertIsNone(_print_check_server_version_results(server_ver_res))

    def test_get_gr_name_from_peer(self):
        """Tests the get_gr_name_from_peer method"""

        self.assertEqual(get_gr_name_from_peer(self.server),
                         "'b7286041-3016-11e6-ba52-507b9d87510a'")

    def test_get_gr_local_address_from(self):
        """Tests the get_gr_local_address_from method"""

        self.assertEqual(get_gr_local_address_from(self.server),
                         "'localhost:3307'")

    def test_get_gr_configs_from_instance(self):
        """Tests the get_gr_configs_from_instance function"""
        res = get_gr_configs_from_instance(self.server)
        # check that result is as expected from the mock server
        self.assertEqual(
            res,
            OrderedDict([('group_replication_group_name', 'name'),
                         ('group_replication_start_on_boot', 'ON'),
                         ('group_replication_group_seeds', '')]))

    def test__print_check_variables_results(self):
        """Tests _print_check_variables_results method"""
        _format_table_results({"pass": True})
        server_var_res = {
            "pass": False,
            "var_name": (True, "expected val", "current val"),
            "var_name2": (False, "exp_val", "cur_val"),
        }
        self.assertTrue(len(_format_table_results(server_var_res)), 2)

    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 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
Exemplo n.º 3
0
class TestGRAdmin(GadgetsTestCase):
    """This class tests the methods in mysql_gadgets.command.gr_admin
    """

    @property
    def num_servers_required(self):
        """Property defining the number of servers required by the test.
        """
        return 1

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

        skip_if_not_GR_approved(self.server)

        if self.server.select_variable(HAVE_SSL) != 'YES':
            raise unittest.SkipTest("Provided server must have_ssl == 'YES'.")

        # User without Create User privileges
        self.server.exec_query("drop user if exists 'nop_user'@'localhost'")
        self.server.exec_query("create user nop_user@'localhost'")
        server_conn2 = self.server_cnx.copy()
        server_conn2["conn_info"] = ("nop_user@localhost:{0}"
                                     "".format(self.server.port))
        self.server2 = Server(self.server_cnx)
        self.server2.connect()

        columns = [MEMBER_ID, MEMBER_HOST, MEMBER_PORT, MEMBER_STATE]
        qry_key = "SELECT {0} FROM {1}".format(", ".join(columns),
                                               REP_GROUP_MEMBERS_TABLE)
        qry_key2 = ("SELECT GROUP_NAME FROM {0} where "
                    "CHANNEL_NAME = 'group_replication_applier'"
                    "".format(REP_CONN_STATUS_TABLE))
        frozen_queries = {
            qry_key: [[], []],
            qry_key2: [],
        }
        self.mock_no_member = get_mock_server(self.server,
                                              queries=frozen_queries)

    def tearDown(self):
        """ Disconnect base server (for all tests).
        """
        # reconnect the server.
        self.server.connect()

        leave(self.server)

        # reconnect the server.
        self.server.connect()

        self.server.exec_query("drop user if exists 'rpl_user'")
        self.server.exec_query("drop user if exists 'nop_user'@'localhost'")
        self.server.exec_query("drop user if exists 'new_rpl_user'")
        self.server.disconnect()
        self.server2.disconnect()

    def test_bootstrap(self):
        """Tests start method
        """
        # Fill the options
        options = {
            "group_name": None,
            "replication_user": None,
            "rep_user_passwd": "passwd",
            "verbose": True
        }

        # test start
        if is_member_of_group(self.server):
            leave(self.server, **options)

        # reconnect the server.
        self.server.connect()

        self.assertTrue(start(self.server, **options))

        # reconnect the server.
        self.server.connect()

        # health
        health(self.server, **options)

        # reconnect the server.
        self.server.connect()

        # Bootstrap active server
        # Trying to start a group with a server that is already a member
        # expect GadgetError: Query failed: is already a member of a GR group.
        with self.assertRaises(GadgetError) as test_raises:
            start(self.server, **options)
        exception = test_raises.exception
        self.assertTrue("is already a member" in exception.errmsg,
                        "The exception message was not the expected. {0}"
                        "".format(exception.errmsg))

        # reconnect the server.
        self.server.connect()

        leave(self.server, **options)

        # reconnect the server.
        self.server.connect()

        group_name = "b7286041-3016-11e6-ba52-507b9d87510a"
        # start using not defaults
        options = {
            "group_name": "b7286041-3016-11e6-ba52-507b9d87510a",
            "gr_host": "{0}:".format(self.server.host),
            "replication_user": "******",
            "rep_user_passwd": "passwd",
            "verbose": True,
            "dry_run": False,
        }

        self.assertTrue(start(self.server, **options))
        # reconnect the server.
        self.server.connect()
        self.assertTrue(is_member_of_group(self.server, group_name))

        self.assertFalse(is_member_of_group(self.server, "group_name"))

        if is_member_of_group(self.server):
            leave(self.server, **options)

        # reconnect the server.
        self.server.connect()

    def test_bootstrap_noselect_priv(self):
        """Tests commands without Select privilege.
        """
        self.server.exec_query("drop user if exists 'not_select'@'%'")
        change_user_privileges(self.server, "not_select", "%",
                               user_passwd="pass", create_user=True)

        server1 = Server({
            "conn_info": "not_select@localhost:{0}".format(self.server.port)})
        server1.passwd = "pass"
        server1.connect()
        # Fill the options
        options = {
            "group_name": "b7286041-3016-11e6-ba52-507b9d87510a",
            "gr_host": ":{0}".format(self.server.port),
            "replication_user": "******",
            "rep_user_passwd": "passwd",
            "verbose": True,
            "dry_run": False,
        }

        # expect GadgetError: Query failed: not have enough privileges to
        #                                   verify server
        with self.assertRaises(GadgetError) as test_raises:
            leave(server1, **options)
        exception = test_raises.exception
        self.assertTrue("not have enough privileges to verify server" in
                        exception.errmsg,
                        "The exception message was not the expected. {0}"
                        "".format(exception.errmsg))

        # reconnect the server.
        server1.connect()

        # expect GadgetError: Query failed: not have enough privileges to
        #                                   verify server
        with self.assertRaises(GadgetError) as test_raises:
            start(server1, **options)
        exception = test_raises.exception
        self.assertTrue("The operation could not continue due to" in
                        exception.errmsg,
                        "The exception message was not the expected. {0}"
                        "".format(exception.errmsg))

        # reconnect the server.
        server1.connect()

        # expect GadgetError: Query failed: not have enough privileges to
        #                                   verify server
        with self.assertRaises(GadgetError) as test_raises:
            health(server1, **options)
        exception = test_raises.exception
        self.assertTrue("not have enough privileges to verify server" in
                        exception.errmsg,
                        "The exception message was not the expected. {0}"
                        "".format(exception.errmsg))

        # reconnect the server.
        server1.connect()

        # expect GadgetError: Query failed: not have enough privileges to
        #                                   verify server
        with self.assertRaises(GadgetError) as test_raises:
            leave(server1, **options)
        exception = test_raises.exception
        self.assertTrue("not have enough privileges to verify server" in
                        exception.errmsg,
                        "The exception message was not the expected. {0}"
                        "".format(exception.errmsg))

    def test_re_bootstrap(self):
        """Tests start method over actively replicating server
        """
        # Fill the options
        options = {
            "group_name": None,
            "replication_user": None,
            "rep_user_passwd": "passwd",
            "gr_host": "",
        }

        # test start
        if is_member_of_group(self.server):
            leave(self.server, **options)

        # reconnect the server.
        self.server.connect()

        self.assertTrue(start(self.server, **options))

        # reconnect the server.
        self.server.connect()

        # test health
        health(self.server, **options)

        # reconnect the server.
        self.server.connect()

        # Trying to start a group with a server that is already a member
        # expect GadgetError: Query failed: is already a member of a GR group.
        with self.assertRaises(GadgetError) as test_raises:
            start(self.server, **options)
        exception = test_raises.exception
        self.assertTrue("is already a member" in exception.errmsg,
                        "The exception message was not the expected. {0}"
                        "".format(exception.errmsg))

        # reconnect the server.
        self.server.connect()

        # Bootstrap with dry-run
        options["dry_run"] = True
        # Trying to start a group with a server that is already a member
        # expect GadgetError: Query failed: is already a member of a GR group.
        with self.assertRaises(GadgetError) as test_raises:
            start(self.server, **options)
        exception = test_raises.exception
        self.assertTrue("is already a member" in exception.errmsg,
                        "The exception message was not the expected. {0}"
                        "".format(exception.errmsg))

        # reconnect the server.
        self.server.connect()

        self.assertTrue(leave(self.server, **options))

        # reconnect the server.
        self.server.connect()

    def test_join(self):
        """Tests join method
        """
        # Fill the options
        options = {
            "group_name": None,
            "gr_host": "{0}:{1}".format(self.server.host, self.server.port),
            "replication_user": "******",
            "rep_user_passwd": "passwd",
            "verbose": False,
            "dry_run": False,
        }

        # join needs start
        if is_member_of_group(self.server):
            leave(self.server, **options)

        # reconnect the server.
        self.server.connect()

        self.assertTrue(start(self.server, **options))

        # reconnect the server.
        self.server.connect()

        # health
        health(self.server, **options)

        # reconnect the server.
        self.server.connect()

        leave(self.server, **options)

        # reconnect the server.
        self.server.connect()

        member_state_qry = ("SELECT MEMBER_STATE FROM {0} as m JOIN {1} "
                            "as s on m.MEMBER_ID = s.MEMBER_ID"
                            "".format(REP_GROUP_MEMBERS_TABLE,
                                      REP_MEMBER_STATS_TABLE))
        frozen_queries = {
            member_state_qry: [['ONLINE', ], ],
        }
        mock_server = get_mock_server(self.server, queries=frozen_queries)

        # Join with defaults ("dry_run": True) and required password
        options = {
            "dry_run": True,
            "replication_user": "******",
            "rep_user_passwd": "passwd",
        }
        join(self.server, mock_server, **options)

        # reconnect the server.
        self.server.connect()

        # Join with no defaults ("dry_run": True)
        options = {
            "group_name": None,
            "gr_host": "{0}:{1}".format(self.server.host, self.server.port),
            "replication_user": "******",
            "rep_user_passwd": "passwd",
            "verbose": False,
            "dry_run": True,
        }
        # leave the group
        if is_member_of_group(self.server):
            leave(self.server, **options)

        self.assertFalse(join(self.server, mock_server, **options))

        # reconnect the server.
        self.server.connect()

        self.assertFalse(leave(self.server, **options))

        # reconnect the server.
        self.server.connect()

    def test_re_join(self):
        """Tests join method over actively replicating server
        """
        # Fill the options
        options = {
            "group_name": None,
            "gr_host": self.server.host,
            "replication_user": "******",
            "rep_user_passwd": "passwd",
            "verbose": False,
            "dry_run": False,
        }

        # test start
        leave(self.server, **options)

        # reconnect the server.
        self.server.connect()

        self.assertTrue(start(self.server, **options))

        # reconnect the server.
        self.server.connect()

        # test health
        health(self.server, **options)

        # reconnect the server.
        self.server.connect()

        options["dry_run"] = True
        # test join
        mock_server = get_mock_server(self.server)
        # Trying to add server to a group while is already a member of a group
        # expect GadgetError: Query failed: is already a member of a GR group.
        with self.assertRaises(GadgetError) as test_raises:
            join(self.server, mock_server, **options)
        exception = test_raises.exception
        self.assertTrue("is already a member" in exception.errmsg,
                        "The exception message was not the expected. {0}"
                        "".format(exception.errmsg))

        # reconnect the server.
        self.server.connect()

        self.assertTrue(leave(self.server, **options))

        # reconnect the server.
        self.server.connect()

    def test_health_with_not_a_member(self):
        """Test the commands that requires a GR member or None value for a
        required server connection information.
        """
        # Fill the options
        options = {}

        # expect GadgetError: Query failed: no server was given
        with self.assertRaises(GadgetError) as test_raises:
            health(None, **options)
        exception = test_raises.exception
        self.assertTrue("No server was given" in exception.errmsg,
                        "The exception message was not the expected. {0}"
                        "".format(exception.errmsg))

        # expect GadgetError: Query failed: not a member of a Group
        with self.assertRaises(GadgetError) as test_raises:
            health(self.mock_no_member, **options)
        exception = test_raises.exception
        self.assertTrue("not a member of a GR group" in exception.errmsg,
                        "The exception message was not the expected. {0}"
                        "".format(exception.errmsg))

        # Fill the options
        options = {
            "group_name": None,
            "replication_user": None,
            "rep_user_passwd": "passwd",
        }

        # expect GadgetError: Query failed: No server was given
        with self.assertRaises(GadgetError) as test_raises:
            start(None, **options)
        exception = test_raises.exception
        self.assertTrue("No server was given" in exception.errmsg,
                        "The exception message was not the expected: {0}"
                        "".format(exception.errmsg))

    def test_join_with_none_values(self):
        """Test the commands that requires a GR member or None value for a
        required server connection information.
        """
        # Fill the options
        options = {
            "group_name": None,
            "replication_user": None,
            "rep_user_passwd": "passwd",
            "gr_host": None,
        }

        # expect GadgetError: Query failed: No peer server was given
        with self.assertRaises(GadgetError) as test_raises:
            join(None, None, **options)
        exception = test_raises.exception
        self.assertTrue("No server was given" in exception.errmsg,
                        "The exception message was not the expected: {0}"
                        "".format(exception.errmsg))

        # expect GadgetError: Query failed: No peer server was given
        with self.assertRaises(GadgetError) as test_raises:
            join(self.server, None, **options)
        exception = test_raises.exception
        self.assertTrue("No peer server provided" in exception.errmsg,
                        "The exception message was not the expected: {0}"
                        "".format(exception.errmsg))

        # reconnect the server.
        self.server.connect()

        # expect GadgetError: Query failed: not a member of a Group
        with self.assertRaises(GadgetError) as test_raises:
            join(self.server, self.mock_no_member, **options)
        exception = test_raises.exception
        self.assertTrue("not a member of a GR group" in exception.errmsg,
                        "The exception message was not the expected: {0}"
                        "".format(exception.errmsg))

        # reconnect the server.
        self.server.connect()

    def test_leave_with_none_values(self):
        """Test the commands that requires a GR member or None value for a
        required server connection information.
        """
        # Fill the options
        options = {}
        # expect GadgetError: Query failed: not a member of a Group
        with self.assertRaises(GadgetError) as test_raises:
            leave(None, **options)
        exception = test_raises.exception
        self.assertTrue("No server was given" in exception.errmsg,
                        "The exception message was not the expected: {0}"
                        "".format(exception.errmsg))

        # expect GadgetError: Query failed: not a member of a Group
        with self.assertRaises(GadgetError) as test_raises:
            leave(self.mock_no_member, **options)
        exception = test_raises.exception
        self.assertTrue("not a member of a Group" in exception.errmsg,
                        "The exception message was not the expected: {0}"
                        "".format(exception.errmsg))

    def test_resolve_gr_local_address(self):
        """Tests resolve_gr_local_address method.
        """
        # Use server host and port
        gr_host = ""
        server_host = self.server.host
        server_port = get_free_random_port()
        self.assertEqual(resolve_gr_local_address(gr_host, server_host,
                                                  server_port),
                         (self.server.host, repr(server_port + 10000)))

        # Use host from gr_host
        gr_host = "host4321"
        server_port = get_free_random_port()
        self.assertEqual(resolve_gr_local_address(gr_host, server_host,
                                                  server_port),
                         ("host4321", repr(server_port + 10000)))

        # Use host from gr_host
        gr_host = "127.0.0.1:"
        server_port = get_free_random_port()
        self.assertEqual(resolve_gr_local_address(gr_host, server_host,
                                                  server_port),
                         ("127.0.0.1", repr(server_port + 10000)))

        # Use given IPv6 host and port from gr_host
        gr_host = "[1:2:3:4:5:6:7:8]:1234"
        self.assertEqual(resolve_gr_local_address(gr_host, server_host,
                                                  server_port),
                         ("1:2:3:4:5:6:7:8", "1234"))

        # Use given IPv6 host and port from gr_host
        server_port = get_free_random_port()
        gr_host = "E3D7::51F4:9BC8:{0}".format(server_port)
        self.assertEqual(resolve_gr_local_address(gr_host, server_host,
                                                  server_port),
                         ("E3D7::51F4:9BC8", repr(server_port)))

        # Use given port from gr_host
        gr_host = "1234"
        server_port = get_free_random_port()
        self.assertEqual(resolve_gr_local_address(gr_host, server_host,
                                                  server_port),
                         (self.server.host, "1234"))

        # Given port on gr_host is out of range, generate a new one instead
        gr_host = "65536"
        self.assertIsInstance(
            int(resolve_gr_local_address(gr_host, server_host,
                                         server_port)[1]), int)

        # Use given port from gr_host
        gr_host = ":1234"
        self.assertEqual(resolve_gr_local_address(gr_host, server_host,
                                                  server_port),
                         (self.server.host, "1234"))

        # Use given IPv6 host from gr_host and generate random port.
        gr_host = "E3D7::51F4:9BC8"
        self.assertEqual(resolve_gr_local_address(gr_host, server_host,
                                                  server_port)[0],
                         "E3D7::51F4:9BC8")
        self.assertIsInstance(
            int(resolve_gr_local_address(gr_host, server_host,
                                         server_port)[1]), int)