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