class TestMySQLMaster(tests.utils.TestCase): """Unit test for the configuration file handling. """ def setUp(self): """Configure the existing environment """ uuid = MySQLServer.discover_uuid(OPTIONS_MASTER["address"]) OPTIONS_MASTER["uuid"] = _uuid.UUID(uuid) self.master = MySQLServer(**OPTIONS_MASTER) self.master.connect() reset_master(self.master) self.master.read_only = True self.master.read_only = False def tearDown(self): """Clean up the existing environment """ cleanup_environment() self.master.disconnect() def test_master_binary_log(self): """Test the get_master_status() function. """ # Note this is only being tested with the binary log. master = self.master # Get master status. check = re.compile('\w+-bin.000001') ret = get_master_status(master) self.assertNotEqual(check.match(ret[0][0]), None) # Reset Master. reset_master(master) ret = get_master_status(master) self.assertTrue(ret[0][1] in (151, 154)) # Format descriptor event. def test_master_health(self): """Test the check_master_issues() function. """ # Note this is only being tested with the binary log. master = self.master # Check health as a master before calling connect. master.disconnect() error, result = check_master_issues(master) expected_result = { 'is_gtid_not_enabled': False, 'is_slave_updates_not_enabled': False, 'is_not_running': True, 'is_binlog_not_enabled': False, 'no_rpl_user': False } self.assertEqual(error, True) self.assertEqual(result, expected_result) # Check health as a master after calling connect. master.connect() error, _ = check_master_issues(master) self.assertEqual(error, False)
def test_wrong_uuid(self): """Test what happens when a server has a wrong uuid. """ # Check wrong uuid. OPTIONS["uuid"] = _uuid.UUID("FD0AC9BB-1431-11E2-8137-11DEF124DCC5") server = MySQLServer(**OPTIONS) self.assertRaises(_errors.UuidError, server.connect) server.disconnect() ConnectionManager().purge_connections(server)
def test_wrong_uuid(self): """Test what happens when a server has a wrong uuid. """ # Check wrong uuid. OPTIONS["uuid"] = _uuid.UUID("FD0AC9BB-1431-11E2-8137-11DEF124DCC5") server = MySQLServer(**OPTIONS) self.assertRaises(_errors.UuidError, server.connect) server.disconnect() ConnectionPool().purge_connections(OPTIONS["uuid"])
class TestMySQLMaster(unittest.TestCase): """Unit test for the configuration file handling. """ def setUp(self): """Configure the existing environment """ uuid = MySQLServer.discover_uuid(OPTIONS_MASTER["address"]) OPTIONS_MASTER["uuid"] = _uuid.UUID(uuid) self.master = MySQLServer(**OPTIONS_MASTER) self.master.connect() reset_master(self.master) self.master.read_only = True self.master.read_only = False def tearDown(self): """Clean up the existing environment """ cleanup_environment() self.master.disconnect() def test_master_binary_log(self): """Test the get_master_status() function. """ # Note this is only being tested with the binary log. master = self.master # Get master status. check = re.compile('\w+-bin.000001') ret = get_master_status(master) self.assertNotEqual(check.match(ret[0][0]), None) # Reset Master. reset_master(master) ret = get_master_status(master) self.assertEqual(int(ret[0][1]), 151) # Format descriptor event. def test_master_health(self): """Test the check_master_issues() function. """ # Note this is only being tested with the binary log. master = self.master # Check health as a master before calling connect. master.disconnect() ret = check_master_issues(master) self.assertEqual(ret, {'is_running': False}) # Check health as a master after calling connect. master.connect() ret = check_master_issues(master) self.assertEqual(ret, {})
def configure_servers(options): """Check if some MySQL's addresses were specified and the number is greater than NUMBER_OF_SERVERS. """ import tests.utils as _test_utils from mysql.fabric.server import ( MySQLServer, ConnectionPool, ) try: servers = _test_utils.MySQLInstances() servers.state_store_address = "{host}:{port}".format( host=options.host, port=options.port ) servers.user = options.db_user servers.passwd = None servers.root_user = options.user servers.root_passwd = options.password if options.servers: for address in options.servers.split(): servers.add_address(address) uuid = MySQLServer.discover_uuid( address=address, user=servers.root_user, passwd=servers.root_passwd ) server = MySQLServer( _uuid.UUID(uuid), address=address, user=servers.root_user, passwd=servers.root_passwd ) server.connect() server.set_session_binlog(False) server.exec_stmt( "GRANT {privileges} ON *.* TO '{user}'@'%%'".format( privileges=", ".join(MySQLServer.ALL_PRIVILEGES), user=servers.user) ) server.exec_stmt("FLUSH PRIVILEGES") server.set_session_binlog(True) server.disconnect() ConnectionPool().purge_connections(server.uuid) if servers.get_number_addresses() < NUMBER_OF_SERVERS: print "<<<<<<<<<< Some unit tests need %s MySQL Instances. " \ ">>>>>>>>>> " % (NUMBER_OF_SERVERS, ) return False except Exception as error: print "Error configuring servers:", error return False return True
def configure_servers(options, config): """Check if some MySQL's addresses were specified and the number is greater than NUMBER_OF_SERVERS. """ import tests.utils as _test_utils from mysql.fabric.server import ( MySQLServer, ConnectionManager, ) from mysql.fabric.backup import ( MySQLDump, ) try: servers = _test_utils.MySQLInstances() # The administrative user as given in --user and --password options. # In simple use cases "root" is used. servers.user = options.user servers.passwd = options.password # Backing store - "fabric_store/storepw". servers.state_store_address = config.get("storage", "address") servers.store_user = config.get("storage", "user") servers.store_passwd = config.get("storage", "password") servers.store_db = config.get("storage", "database") # Server user - "fabric_server/serverpw". servers.server_user = config.get("servers", "user") servers.server_passwd = config.get("servers", "password") # Backup user - "fabric_backup/backuppw". servers.backup_user = config.get("servers", "backup_user") servers.backup_passwd = config.get("servers", "backup_password") # Restore user - "fabric_restore/restorepw". servers.restore_user = config.get("servers", "restore_user") servers.restore_passwd = config.get("servers", "restore_password") # Set up the backing store. from mysql.fabric import persistence uuid = MySQLServer.discover_uuid( address=servers.state_store_address, user=servers.user, passwd=servers.passwd ) server = MySQLServer( _uuid.UUID(uuid), address=servers.state_store_address, user=servers.user, passwd=servers.passwd ) server.connect() # Precautionary cleanup. server.exec_stmt("DROP DATABASE IF EXISTS %s" % (servers.store_db,)) # Create store user. _test_utils.create_test_user( server, servers.store_user, servers.store_passwd, [(persistence.required_privileges(), "{db}.*".format(db=servers.store_db))] ) # Set up managed servers. if options.servers: for address in options.servers.split(): servers.add_address(address) uuid = MySQLServer.discover_uuid( address=address, user=servers.user, passwd=servers.passwd ) server = MySQLServer( _uuid.UUID(uuid), address=address, user=servers.user, passwd=servers.passwd ) server.connect() server.set_session_binlog(False) server.read_only = False # Drop user databases server.set_foreign_key_checks(False) databases = server.exec_stmt("SHOW DATABASES") for database in databases: if database[0] not in MySQLServer.NO_USER_DATABASES: server.exec_stmt("DROP DATABASE IF EXISTS %s" % (database[0],)) server.set_foreign_key_checks(True) # Create server user. _test_utils.create_test_user( server, servers.server_user, servers.server_passwd, [(MySQLServer.SERVER_PRIVILEGES, "*.*"), (MySQLServer.SERVER_PRIVILEGES_DB, "mysql_fabric.*")] ) # Create backup user. _test_utils.create_test_user( server, servers.backup_user, servers.backup_passwd, [(MySQLDump.BACKUP_PRIVILEGES, "*.*")] ) # Create restore user. _test_utils.create_test_user( server, servers.restore_user, servers.restore_passwd, [(MySQLDump.RESTORE_PRIVILEGES, "*.*")] ) server.set_session_binlog(True) server.disconnect() ConnectionManager().purge_connections(server) if servers.get_number_addresses() < NUMBER_OF_SERVERS: sys.stderr.write( "<<<<<<<<<< Some unit tests need {0} MySQL " \ "Instances. >>>>>>>>>>\n".format(NUMBER_OF_SERVERS) ) return False except Exception as error: sys.stderr.write( "Error configuring servers: {0}\n".format(str(error)) ) import traceback traceback.print_exc() return False return True
class TestErrorLog(unittest.TestCase): """Unit test for testing ErrorLog. """ def setUp(self): """Configure the existing environment """ uuid = MySQLServer.discover_uuid( tests.utils.MySQLInstances().get_address(0) ) self.server = MySQLServer(_uuid.UUID(uuid), tests.utils.MySQLInstances().get_address(0) ) MySQLServer.add(self.server) def tearDown(self): """Clean up the existing environment """ tests.utils.cleanup_environment() self.server.disconnect() MySQLServer.remove(self.server) def test_init(self): """Test basic properties/methods in the ErrorLog. """ # Check that the input parameters are not changed. interval = get_time_delta(1) now = get_time() input_whens = [ 30, 40 ] input_reporters = [ "reporter", "reporter" ] st = ErrorLog(self.server, interval, now, input_whens, input_reporters) self.assertEqual(st.server_uuid, self.server.uuid) self.assertEqual(st.interval, interval) self.assertEqual(st.now, now) self.assertEqual(st.whens, input_whens) self.assertEqual(st.reporters, input_reporters) # If whens and reporters don't have the same length, an exception is # raised interval = get_time_delta(1) now = get_time() input_whens = [ 0, 0, 0, 0 ] input_reporters = [] self.assertRaises(AssertionError, ErrorLog, self.server, interval, now, input_whens, input_reporters) def test_persistence(self): """Test ErrorLog. """ # Update/Notify and fetch, they should match. interval = get_time_delta(1) now = get_time() input_whens = [ now, now ] input_reporters = [ "client:1000", "client:2000" ] st = ErrorLog(self.server, interval, now, input_whens, input_reporters) ErrorLog.add(self.server, now, "client:1000", "error") ErrorLog.add(self.server, now, "client:2000", "error") new_st = ErrorLog.fetch(self.server, interval, now) self.assertEqual(st.reporters, new_st.reporters) self.assertEqual(st.whens, new_st.whens) # Call remove, they should be empty and match. interval = get_time_delta(1) now = get_time() input_whens = [ ] input_reporters = [ ] ErrorLog.remove(self.server) st = ErrorLog(self.server, interval, now, input_whens, input_reporters) new_st = ErrorLog.fetch(self.server, interval, now) self.assertEqual(st.reporters, new_st.reporters) self.assertEqual(st.whens, new_st.whens) # Update/Notify and refresh, they should match. interval = get_time_delta(10) now = get_time() input_whens = [ now, now - get_time_delta(5) ] input_reporters = [ "client:1000", "client:2000" ] st = ErrorLog(self.server, interval, now, [], []) ErrorLog.add(self.server, now, "client:1000", "error") ErrorLog.add(self.server, now - get_time_delta(5), "client:2000", "error") ErrorLog.add(self.server, now - get_time_delta(11), "client:3000", "error") st.refresh() self.assertEqual(set(st.reporters), set(input_reporters)) self.assertEqual(set(st.whens), set(input_whens)) # Check whether a statement similar to the one used in the # event is fine. ErrorLog.remove(self.server) ErrorLog.add(self.server, now, "client:1000", "error") ErrorLog.add(self.server, now, "client:2000", "error") persister = _persistence.current_persister() out = persister.exec_stmt( "SELECT reported, UTC_TIMESTAMP() as now, " "TIMEDIFF(UTC_TIMESTAMP(), reported - MAKETIME(2,0,0)) as diff " "FROM error_log" ) _LOGGER.debug("Output test persistence %s.", out) self.assertEqual(len(out), 2) res = persister.exec_stmt( "DELETE FROM error_log WHERE " "TIMEDIFF(UTC_TIMESTAMP(), reported - MAKETIME(2,0,0)) > " "MAKETIME(1,0,0)" ) _LOGGER.debug("Output test persistence %s.", res) out = persister.exec_stmt( "SELECT reported, UTC_TIMESTAMP() as now, " "TIMEDIFF(UTC_TIMESTAMP(), reported - MAKETIME(2,0,0)) as diff " "FROM error_log" ) _LOGGER.debug("Output test persistence %s.", out) self.assertEqual(len(out), 0) def test_check_instability(self): """Test whether a server can be considered unstable or not. """ # Update/Notify and refresh, they should match. interval = get_time_delta(10) now = get_time() input_whens = [ now, now - get_time_delta(5) ] input_reporters = [ "client:1000", "client:2000" ] st = ErrorLog(self.server, interval, now, [], []) ErrorLog.add(self.server, now, "client:1000", "error") ErrorLog.add(self.server, now - get_time_delta(5), "client:2000", "error") ErrorLog.add(self.server, now - get_time_delta(11), "client:3000", "error") st.refresh() self.assertEqual( st.is_unstable(n_notifications=1, n_reporters=1, filter_reporter=None), True ) self.assertEqual( st.is_unstable(n_notifications=2, n_reporters=2, filter_reporter=None), True ) self.assertEqual( st.is_unstable(n_notifications=3, n_reporters=2, filter_reporter=None), False ) self.assertEqual( st.is_unstable(n_notifications=2, n_reporters=3, filter_reporter=None), False ) self.assertEqual( st.is_unstable(n_notifications=1, n_reporters=1, filter_reporter=["client:2000"]), True )
class TestMySQLServer(unittest.TestCase): """Unit test for testing MySQLServer. """ def setUp(self): """Configure the existing environment """ uuid = MySQLServer.discover_uuid(OPTIONS["address"]) OPTIONS["uuid"] = _uuid.UUID(uuid) self.server = MySQLServer(**OPTIONS) MySQLServer.add(self.server) def tearDown(self): """Clean up the existing environment """ tests.utils.cleanup_environment() self.server.disconnect() MySQLServer.remove(self.server) def test_wrong_uuid(self): """Test what happens when a server has a wrong uuid. """ # Check wrong uuid. OPTIONS["uuid"] = _uuid.UUID("FD0AC9BB-1431-11E2-8137-11DEF124DCC5") server = MySQLServer(**OPTIONS) self.assertRaises(_errors.UuidError, server.connect) server.disconnect() ConnectionManager().purge_connections(server) def test_properties(self): """Test setting MySQLServer's properties. """ server = self.server # Check property user. self.assertEqual(server.user, tests.utils.MySQLInstances().user) server.user = "******" self.assertEqual(server.user, "user") server.user = tests.utils.MySQLInstances().user # Check property passwd. self.assertEqual(server.passwd, tests.utils.MySQLInstances().passwd) server.passwd = "passwd" self.assertEqual(server.passwd, "passwd") server.passwd = tests.utils.MySQLInstances().passwd # Check property status. self.assertEqual(server.status, MySQLServer.SECONDARY) server.status = MySQLServer.FAULTY self.assertEqual(server.status, MySQLServer.FAULTY) fetched_server = MySQLServer.fetch(server.uuid) self.assertEqual(server.status, fetched_server.status) server.status = MySQLServer.SECONDARY fetched_server = MySQLServer.fetch(server.uuid) self.assertEqual(server.status, fetched_server.status) # Check property mode. self.assertEqual(server.mode, MySQLServer.READ_ONLY) server.mode = MySQLServer.OFFLINE self.assertEqual(server.mode, MySQLServer.OFFLINE) fetched_server = MySQLServer.fetch(server.uuid) self.assertEqual(server.mode, fetched_server.mode) server.mode = MySQLServer.READ_ONLY fetched_server = MySQLServer.fetch(server.uuid) self.assertEqual(server.mode, fetched_server.mode) # Check property weight. self.assertEqual(server.weight, MySQLServer.DEFAULT_WEIGHT) server.weight = 0.1 self.assertEqual(server.weight, 0.1) fetched_server = MySQLServer.fetch(server.uuid) self.assertEqual(server.weight, fetched_server.weight) server.weight = MySQLServer.DEFAULT_WEIGHT fetched_server = MySQLServer.fetch(server.uuid) self.assertEqual(server.weight, fetched_server.weight) # Create instance without connecting it with a server. self.assertEqual(server.read_only, None) self.assertEqual(server.server_id, None) self.assertEqual(server.gtid_enabled, None) self.assertEqual(server.binlog_enabled, None) self.assertEqual(server.version, None) # Bind instance to a server. server.connect() self.assertNotEqual(server.read_only, None) self.assertNotEqual(server.server_id, 0) self.assertEqual(server.gtid_enabled, True) self.assertEqual(server.binlog_enabled, True) # Check read_only property. server.read_only = True self.assertEqual(server.read_only, True) server.read_only = False self.assertEqual(server.read_only, False) def test_address(self): """Check whether the address is automatically converted to the format host:port where port is the MySQL default port if a port is not provided. """ server = MySQLServer(_uuid.uuid4(), "address") default_port = _server_utils.MYSQL_DEFAULT_PORT self.assertEqual(server.address, "{0}:{1}".format("address", default_port)) server = MySQLServer(_uuid.uuid4(), "address:port") self.assertEqual(server.address, "{0}:{1}".format("address", "port")) def test_version(self): """Check MySQLServer's version. """ server = self.server server.connect() # Check version. self.assertFalse(server.check_version_compat((7, 0, 0))) self.assertFalse(server.check_version_compat((6, 0, 0))) # Note this function needs to be updated to update every # time a new release is created. def test_gtid(self): """Check MySQLServer's GTIDs. """ server = self.server server.connect() for record in server.get_gtid_status(): self.assertEqual(record.GTID_EXECUTED, "") self.assertEqual(record.GTID_PURGED, "") self.assertEqual(record.GTID_OWNED, "") # Note this is only being tested with GTIDs. def test_kill_connections(self): """Check that the kill method successfully kills existing connections on the server. """ #check that the current connection is not terminated. server = self.server server.connect() server.kill_processes(server.processes()) self.assertTrue(server.has_storage_engine("Innodb")) #check that the only connection remaining is the current #connection server.kill_processes(server.processes(True, False)) self.assertEqual(len(server.processes(True, True)), 1) def test_storage(self): """Check MySQLServer's storage. """ server = self.server server.connect() self.assertTrue(server.has_storage_engine("Innodb")) self.assertTrue(server.has_storage_engine("")) self.assertFalse(server.has_storage_engine("Unknown")) def test_session_properties(self): """Test some session's properties. """ server = self.server server.connect() server.set_session_binlog(False) self.assertFalse(server.session_binlog_enabled()) server.set_session_binlog(True) self.assertTrue(server.session_binlog_enabled()) server.set_foreign_key_checks(False) self.assertFalse(server.foreign_key_checks_enabled()) server.set_foreign_key_checks(True) self.assertTrue(server.foreign_key_checks_enabled()) def test_binlog(self): """Test binary logging is supported. """ server = self.server server.connect() check = re.compile('\w+-bin.000001') for record in server.get_binary_logs(): self.assertNotEqual(check.match(record.Log_name), None) # Note this is only being tested with the binary log. def test_exec_stmt_options(self): """Test statement's execution. """ server = self.server server.connect() # Populate testing tables. server.exec_stmt("CREATE DATABASE IF NOT EXISTS test") server.exec_stmt("USE test") server.exec_stmt("DROP TABLE IF EXISTS test_1") server.exec_stmt("CREATE TABLE test_1(id INTEGER)") server.exec_stmt("DROP TABLE IF EXISTS test_2") server.exec_stmt("CREATE TABLE test_2(id INTEGER)") for cont in range(1, 10): server.exec_stmt("INSERT INTO test_1 VALUES(%s)", {"params": (cont, )}) # Test raw: True fetch : True ret = server.exec_stmt("SELECT COUNT(*) FROM test_1", { "raw": True, "fetch": True }) self.assertEqual(int(ret[0][0]), 9) # Test raw: False fetch : True ret = server.exec_stmt("SELECT COUNT(*) FROM test_1", { "raw": False, "fetch": True }) self.assertEqual(ret[0][0], 9) # Test raw: False fetch : False cursor = server.exec_stmt("SELECT COUNT(*) FROM test_1", { "raw": False, "fetch": False }) ret = cursor.fetchone() self.assertEqual(ret[0], 9) # Test raw: True fetch : False cursor = server.exec_stmt("SELECT COUNT(*) FROM test_1", { "raw": False, "fetch": False }) ret = cursor.fetchone() self.assertEqual(int(ret[0]), 9) # Nothing to be fetched. ret = server.exec_stmt("SELECT * FROM test_2") self.assertEqual(ret, []) # Unknown table. self.assertRaises(_errors.DatabaseError, server.exec_stmt, "SELECT * FROM test_3") # Test option columns ret = server.exec_stmt("SELECT COUNT(*) COUNT FROM test_1", {"columns": True}) self.assertEqual(int(ret[0][0]), 9) self.assertEqual(int(ret[0].COUNT), 9) def test_is_connected(self): """Check whether MySQLServer is alive or not. """ # Check if server is alive. server = self.server self.assertTrue(MySQLServer.is_alive(server)) self.assertFalse(server.is_connected()) server.connect() self.assertTrue(server.is_connected()) def test_utilities(self): """Check MySQLServer's utilities module. """ # Test a function that gets host and port and returns # host:port address = _server_utils.combine_host_port(None, None, 3306) self.assertEqual(address, "unknown host:3306") address = _server_utils.combine_host_port("", None, 3306) self.assertEqual(address, "unknown host:3306") address = _server_utils.combine_host_port(None, "", 3306) self.assertEqual(address, "unknown host:3306") address = _server_utils.combine_host_port("host", "port", 3306) self.assertEqual(address, "host:port") address = _server_utils.combine_host_port("host", 1500, 3306) self.assertEqual(address, "host:1500") address = _server_utils.combine_host_port("127.0.0.1", 1500, 3306) self.assertEqual(address, "localhost:1500") # Test a function that gets host:port and returns (host, port) host_port = _server_utils.split_host_port("", 3306) self.assertEqual(host_port, ("", 3306)) host_port = _server_utils.split_host_port(":", 3306) self.assertEqual(host_port, ("", "")) host_port = _server_utils.split_host_port("host:", 3306) self.assertEqual(host_port, ("host", "")) host_port = _server_utils.split_host_port(":port", 3306) self.assertEqual(host_port, ("", "port")) host_port = _server_utils.split_host_port("host:port", 3306) self.assertEqual(host_port, ("host", "port")) def test_server_id(self): """Test MySQLServer's uuid. """ # Configure uuid = MySQLServer.discover_uuid(OPTIONS["address"]) OPTIONS["uuid"] = _uuid.UUID(uuid) server_1 = MySQLServer(**OPTIONS) server_2 = MySQLServer(**OPTIONS) # Check that two different objects represent the same server. self.assertEqual(server_1, server_2) # Check that a dictionary with server_1 and server_2 has in # fact only one entry. hash_info = {} hash_info[server_1] = server_1 hash_info[server_2] = server_2 self.assertEqual(len(hash_info), 1) def test_persister_id(self): """Test Persister's uuid. """ # Get persister'a address. instances = tests.utils.MySQLInstances() address = instances.state_store_address user = instances.store_user passwd = instances.store_passwd # Try to manage the MySQLPersister. uuid = MySQLServer.discover_uuid(address=address, user=user, passwd=passwd) server = MySQLServer(_uuid.UUID(uuid), address, user, passwd) self.assertRaises(_errors.ServerError, MySQLServer.add, server) def test_privileges(self): """Test whether user's have the appropriate privileges. """ # Some privileges MINIMUM_PRIVILEGES = [ "REPLICATION SLAVE", "REPLICATION CLIENT", "SUPER", "SHOW DATABASES", "RELOAD" ] # Connect to server as admin and create temporary user. uuid = MySQLServer.discover_uuid(OPTIONS["address"]) server = MySQLServer(_uuid.UUID(uuid), OPTIONS["address"], tests.utils.MySQLInstances().user, tests.utils.MySQLInstances().passwd) ConnectionManager().purge_connections(server) server.connect() server.set_session_binlog(False) server.exec_stmt("CREATE USER 'jeffrey'@'%%' IDENTIFIED BY 'mypass'") # Check if jeffrey (temporary user) has the appropriate privileges. # There is not privilege associate to jeffrey. new_server = MySQLServer(_uuid.UUID(uuid), OPTIONS["address"], "jeffrey", "mypass") new_server.connect() self.assertFalse(new_server.has_privileges(MINIMUM_PRIVILEGES)) # Check if jeffrey (temporary user) has the appropriate privileges. # Grant required privileges except RELOAD # There is no RELOAD on a global level. privileges = ", ".join( [priv for priv in MINIMUM_PRIVILEGES if priv != "RELOAD"]) server.exec_stmt("GRANT {privileges} ON *.* TO 'jeffrey'@'%%'".format( privileges=privileges)) self.assertFalse(new_server.has_privileges(MINIMUM_PRIVILEGES)) # Check if jeffrey (temporary user) has the appropriate privileges. # The RELOAD on a global level was granted. server.exec_stmt("GRANT RELOAD ON *.* TO 'jeffrey'@'%%'") self.assertTrue(new_server.has_privileges(MINIMUM_PRIVILEGES)) # Check if jeffrey (temporary user) has the appropriate privileges. # Revoke privilegs from temporary user. # There is no ALL on a global level. server.exec_stmt("REVOKE ALL PRIVILEGES, GRANT OPTION FROM " "'jeffrey'@'%%'") server.exec_stmt("GRANT ALL ON fabric.* TO 'jeffrey'@'%%'") self.assertFalse(new_server.has_privileges(MINIMUM_PRIVILEGES)) # Check if jeffrey (temporary user) has the appropriate privileges. # Grant all privileges, which the administrative user has to have. server.exec_stmt("GRANT" " ALTER, ALTER ROUTINE, CREATE, CREATE ROUTINE," " CREATE TEMPORARY TABLES, CREATE USER," " CREATE VIEW, DELETE, DROP, EVENT, EXECUTE," " GRANT OPTION, INDEX, INSERT, LOCK TABLES, PROCESS, " " RELOAD, REPLICATION CLIENT, REPLICATION SLAVE," " SELECT, SHOW DATABASES, SHOW VIEW, SHUTDOWN," " SUPER, TRIGGER, UPDATE" " ON *.* TO 'jeffrey'@'%%'") self.assertTrue(new_server.has_privileges(MINIMUM_PRIVILEGES)) # Drop temporary user. server.exec_stmt("DROP USER 'jeffrey'@'%%'") server.set_session_binlog(True) server.disconnect() new_server.disconnect() ConnectionManager().purge_connections(server) def test_max_connections(self): uuid = MySQLServer.discover_uuid(OPTIONS["address"]) server = MySQLServer( _uuid.UUID(uuid), OPTIONS["address"], ) server.connect() res = server.get_variable("max_connections") self.assertNotEqual(int(res), 0)
def test_privileges(self): """Test whether user's have the appropriate privileges. """ # Some privileges MINIMUM_PRIVILEGES = [ "REPLICATION SLAVE", "REPLICATION CLIENT", "SUPER", "SHOW DATABASES", "RELOAD" ] # Connect to server as admin and create temporary user. uuid = MySQLServer.discover_uuid(OPTIONS["address"]) server = MySQLServer(_uuid.UUID(uuid), OPTIONS["address"], tests.utils.MySQLInstances().user, tests.utils.MySQLInstances().passwd) ConnectionManager().purge_connections(server) server.connect() server.set_session_binlog(False) server.exec_stmt("CREATE USER 'jeffrey'@'%%' IDENTIFIED BY 'mypass'") # Check if jeffrey (temporary user) has the appropriate privileges. # There is not privilege associate to jeffrey. new_server = MySQLServer(_uuid.UUID(uuid), OPTIONS["address"], "jeffrey", "mypass") new_server.connect() self.assertFalse(new_server.has_privileges(MINIMUM_PRIVILEGES)) # Check if jeffrey (temporary user) has the appropriate privileges. # Grant required privileges except RELOAD # There is no RELOAD on a global level. privileges = ", ".join( [priv for priv in MINIMUM_PRIVILEGES if priv != "RELOAD"]) server.exec_stmt("GRANT {privileges} ON *.* TO 'jeffrey'@'%%'".format( privileges=privileges)) self.assertFalse(new_server.has_privileges(MINIMUM_PRIVILEGES)) # Check if jeffrey (temporary user) has the appropriate privileges. # The RELOAD on a global level was granted. server.exec_stmt("GRANT RELOAD ON *.* TO 'jeffrey'@'%%'") self.assertTrue(new_server.has_privileges(MINIMUM_PRIVILEGES)) # Check if jeffrey (temporary user) has the appropriate privileges. # Revoke privilegs from temporary user. # There is no ALL on a global level. server.exec_stmt("REVOKE ALL PRIVILEGES, GRANT OPTION FROM " "'jeffrey'@'%%'") server.exec_stmt("GRANT ALL ON fabric.* TO 'jeffrey'@'%%'") self.assertFalse(new_server.has_privileges(MINIMUM_PRIVILEGES)) # Check if jeffrey (temporary user) has the appropriate privileges. # Grant all privileges, which the administrative user has to have. server.exec_stmt("GRANT" " ALTER, ALTER ROUTINE, CREATE, CREATE ROUTINE," " CREATE TEMPORARY TABLES, CREATE USER," " CREATE VIEW, DELETE, DROP, EVENT, EXECUTE," " GRANT OPTION, INDEX, INSERT, LOCK TABLES, PROCESS, " " RELOAD, REPLICATION CLIENT, REPLICATION SLAVE," " SELECT, SHOW DATABASES, SHOW VIEW, SHUTDOWN," " SUPER, TRIGGER, UPDATE" " ON *.* TO 'jeffrey'@'%%'") self.assertTrue(new_server.has_privileges(MINIMUM_PRIVILEGES)) # Drop temporary user. server.exec_stmt("DROP USER 'jeffrey'@'%%'") server.set_session_binlog(True) server.disconnect() new_server.disconnect() ConnectionManager().purge_connections(server)
class TestMySQLServer(unittest.TestCase): """Unit test for testing MySQLServer. """ def setUp(self): """Configure the existing environment """ uuid = MySQLServer.discover_uuid(OPTIONS["address"]) OPTIONS["uuid"] = _uuid.UUID(uuid) self.server = MySQLServer(**OPTIONS) MySQLServer.add(self.server) def tearDown(self): """Clean up the existing environment """ tests.utils.cleanup_environment() self.server.disconnect() MySQLServer.remove(self.server) def test_wrong_uuid(self): """Test what happens when a server has a wrong uuid. """ # Check wrong uuid. OPTIONS["uuid"] = _uuid.UUID("FD0AC9BB-1431-11E2-8137-11DEF124DCC5") server = MySQLServer(**OPTIONS) self.assertRaises(_errors.UuidError, server.connect) server.disconnect() ConnectionPool().purge_connections(OPTIONS["uuid"]) def test_properties(self): """Test setting MySQLServer's properties. """ server = self.server # Check property user. self.assertEqual(server.user, tests.utils.MySQLInstances().user) server.user = "******" self.assertEqual(server.user, "user") server.user = tests.utils.MySQLInstances().user # Check property passwd. self.assertEqual(server.passwd, tests.utils.MySQLInstances().passwd) server.passwd = "passwd" self.assertEqual(server.passwd, "passwd") server.passwd = tests.utils.MySQLInstances().passwd # Check property status. self.assertEqual(server.status, MySQLServer.SECONDARY) server.status = MySQLServer.FAULTY self.assertEqual(server.status, MySQLServer.FAULTY) fetched_server = MySQLServer.fetch(server.uuid) self.assertEqual(server.status, fetched_server.status) server.status = MySQLServer.SECONDARY fetched_server = MySQLServer.fetch(server.uuid) self.assertEqual(server.status, fetched_server.status) # Check property mode. self.assertEqual(server.mode, MySQLServer.READ_ONLY) server.mode = MySQLServer.OFFLINE self.assertEqual(server.mode, MySQLServer.OFFLINE) fetched_server = MySQLServer.fetch(server.uuid) self.assertEqual(server.mode, fetched_server.mode) server.mode = MySQLServer.READ_ONLY fetched_server = MySQLServer.fetch(server.uuid) self.assertEqual(server.mode, fetched_server.mode) # Check property weight. self.assertEqual(server.weight, MySQLServer.DEFAULT_WEIGHT) server.weight = 0.1 self.assertEqual(server.weight, 0.1) fetched_server = MySQLServer.fetch(server.uuid) self.assertEqual(server.weight, fetched_server.weight) server.weight = MySQLServer.DEFAULT_WEIGHT fetched_server = MySQLServer.fetch(server.uuid) self.assertEqual(server.weight, fetched_server.weight) # Create instance without connecting it with a server. self.assertEqual(server.read_only, None) self.assertEqual(server.server_id, None) self.assertEqual(server.gtid_enabled, None) self.assertEqual(server.binlog_enabled, None) self.assertEqual(server.version, None) # Bind instance to a server. server.connect() self.assertNotEqual(server.read_only, None) self.assertNotEqual(server.server_id, 0) self.assertEqual(server.gtid_enabled, True) self.assertEqual(server.binlog_enabled, True) # Check read_only property. server.read_only = True self.assertEqual(server.read_only, True) server.read_only = False self.assertEqual(server.read_only, False) def test_version(self): """Check MySQLServer's version. """ server = self.server server.connect() # Check version. self.assertFalse(server.check_version_compat((7, 0, 0))) self.assertFalse(server.check_version_compat((6, 0, 0))) # Note this function needs to be updated to update every # time a new release is created. def test_gtid(self): """Check MySQLServer's GTIDs. """ server = self.server server.connect() for record in server.get_gtid_status(): self.assertEqual(record.GTID_EXECUTED, "") self.assertEqual(record.GTID_PURGED, "") self.assertEqual(record.GTID_OWNED, "") # Note this is only being tested with GTIDs. def test_storage(self): """Check MySQLServer's storage. """ server = self.server server.connect() self.assertTrue(server.has_storage_engine("Innodb")) self.assertTrue(server.has_storage_engine("")) self.assertFalse(server.has_storage_engine("Unknown")) def test_session_properties(self): """Test some session's properties. """ server = self.server server.connect() server.set_session_binlog(False) self.assertFalse(server.session_binlog_enabled()) server.set_session_binlog(True) self.assertTrue(server.session_binlog_enabled()) server.set_foreign_key_checks(False) self.assertFalse(server.foreign_key_checks_enabled()) server.set_foreign_key_checks(True) self.assertTrue(server.foreign_key_checks_enabled()) def test_binlog(self): """Test binary logging is supported. """ server = self.server server.connect() check = re.compile('\w+-bin.000001') for record in server.get_binary_logs(): self.assertNotEqual(check.match(record.Log_name), None) # Note this is only being tested with the binary log. def test_exec_stmt_options(self): """Test statement's execution. """ server = self.server server.connect() # Populate testing tables. server.exec_stmt("CREATE DATABASE IF NOT EXISTS test") server.exec_stmt("USE test") server.exec_stmt("DROP TABLE IF EXISTS test_1") server.exec_stmt("CREATE TABLE test_1(id INTEGER)") server.exec_stmt("DROP TABLE IF EXISTS test_2") server.exec_stmt("CREATE TABLE test_2(id INTEGER)") for cont in range(1, 10): server.exec_stmt("INSERT INTO test_1 VALUES(%s)", {"params" : (cont,)}) # Test raw: True fetch : True ret = server.exec_stmt("SELECT COUNT(*) FROM test_1", {"raw" : True, "fetch" : True}) self.assertEqual(int(ret[0][0]), 9) # Test raw: False fetch : True ret = server.exec_stmt("SELECT COUNT(*) FROM test_1", {"raw" : False, "fetch" : True}) self.assertEqual(ret[0][0], 9) # Test raw: False fetch : False cursor = server.exec_stmt("SELECT COUNT(*) FROM test_1", {"raw" : False, "fetch" : False}) ret = cursor.fetchone() self.assertEqual(ret[0], 9) # Test raw: True fetch : False cursor = server.exec_stmt("SELECT COUNT(*) FROM test_1", {"raw" : False, "fetch" : False}) ret = cursor.fetchone() self.assertEqual(int(ret[0]), 9) # Nothing to be fetched. ret = server.exec_stmt("SELECT * FROM test_2") self.assertEqual(ret, []) # Unknown table. self.assertRaises(_errors.DatabaseError, server.exec_stmt, "SELECT * FROM test_3") # Test option columns ret = server.exec_stmt("SELECT COUNT(*) COUNT FROM test_1", {"columns" : True}) self.assertEqual(int(ret[0][0]), 9) self.assertEqual(int(ret[0].COUNT), 9) def test_is_connected(self): """Check whether MySQLServer is alive or not. """ # Check if server is alive. server = self.server self.assertTrue(server.is_alive()) self.assertFalse(server.is_connected()) server.connect() self.assertTrue(server.is_connected()) def test_utilities(self): """Check MySQLServer's utilities module. """ # Test a function that gets host and port and returns # host:port address = _server_utils.combine_host_port(None, None, 3306) self.assertEqual(address, "unknown host:3306") address = _server_utils.combine_host_port("", None, 3306) self.assertEqual(address, "unknown host:3306") address = _server_utils.combine_host_port(None, "", 3306) self.assertEqual(address, "unknown host:3306") address = _server_utils.combine_host_port("host", "port", 3306) self.assertEqual(address, "host:port") address = _server_utils.combine_host_port("host", 1500, 3306) self.assertEqual(address, "host:1500") address = _server_utils.combine_host_port("127.0.0.1", 1500, 3306) self.assertEqual(address, "localhost:1500") # Test a function that gets host:port and returns (host, port) host_port = _server_utils.split_host_port("", 3306) self.assertEqual(host_port, ("", 3306)) host_port = _server_utils.split_host_port(":", 3306) self.assertEqual(host_port, ("", "")) host_port = _server_utils.split_host_port("host:", 3306) self.assertEqual(host_port, ("host", "")) host_port = _server_utils.split_host_port(":port", 3306) self.assertEqual(host_port, ("", "port")) host_port = _server_utils.split_host_port("host:port", 3306) self.assertEqual(host_port, ("host", "port")) def test_server_id(self): """Test MySQLServer's uuid. """ # Configure uuid = MySQLServer.discover_uuid(OPTIONS["address"]) OPTIONS["uuid"] = _uuid.UUID(uuid) server_1 = MySQLServer(**OPTIONS) server_2 = MySQLServer(**OPTIONS) # Check that two different objects represent the same server. self.assertEqual(server_1, server_2) # Check that a dictionary with server_1 and server_2 has in # fact only one entry. hash_info = {} hash_info[server_1] = server_1 hash_info[server_2] = server_2 self.assertEqual(len(hash_info), 1) def test_persister_id(self): """Test Persister's uuid. """ # Get persister'a address. instances = tests.utils.MySQLInstances() address = instances.state_store_address user = instances.root_user passwd = instances.root_passwd # Try to manage the MySQLPersister. uuid = MySQLServer.discover_uuid( address=address, user=user, passwd=passwd ) server = MySQLServer(_uuid.UUID(uuid), address, user, passwd) self.assertRaises(_errors.ServerError, MySQLServer.add, server) def test_privileges(self): """Test whether user's have the appropriate privileges. """ # Some privileges MINIMUM_PRIVILEGES = [ "REPLICATION SLAVE", "REPLICATION CLIENT", "SUPER", "SHOW DATABASES", "RELOAD" ] # Connect to server as root and create temporary user. uuid = MySQLServer.discover_uuid(OPTIONS["address"]) server = MySQLServer( _uuid.UUID(uuid), OPTIONS["address"], tests.utils.MySQLInstances().root_user, tests.utils.MySQLInstances().root_passwd ) ConnectionPool().purge_connections(_uuid.UUID(uuid)) server.connect() server.set_session_binlog(False) server.exec_stmt( "CREATE USER 'jeffrey'@'%%' IDENTIFIED BY 'mypass'" ) # Check if jeffrey (temporary user) has the appropriate privileges. # There is not privilege associate to jeffrey. new_server = MySQLServer( _uuid.UUID(uuid), OPTIONS["address"], "jeffrey", "mypass" ) new_server.connect() self.assertFalse( new_server.has_privileges(MINIMUM_PRIVILEGES) ) # Check if jeffrey (temporary user) has the appropriate privileges. # Grant required privileges except RELOAD # There is no RELOAD on a global level. privileges=", ".join([priv for priv in MINIMUM_PRIVILEGES if priv != "RELOAD"] ) server.exec_stmt( "GRANT {privileges} ON *.* TO 'jeffrey'@'%%'".format( privileges=privileges) ) server.exec_stmt("FLUSH PRIVILEGES") self.assertFalse( new_server.has_privileges(MINIMUM_PRIVILEGES) ) # Check if jeffrey (temporary user) has the appropriate privileges. # The RELOAD on a global level was granted. server.exec_stmt("GRANT RELOAD ON *.* TO 'jeffrey'@'%%'") server.exec_stmt("FLUSH PRIVILEGES") self.assertTrue( new_server.has_privileges(MINIMUM_PRIVILEGES) ) # Check if jeffrey (temporary user) has the appropriate privileges. # Revoke privilegs from temporary user. # There is no ALL on a global level. server.exec_stmt("REVOKE ALL PRIVILEGES, GRANT OPTION FROM " "'jeffrey'@'%%'" ) server.exec_stmt("GRANT ALL ON fabric.* TO 'jeffrey'@'%%'") server.exec_stmt("FLUSH PRIVILEGES") self.assertFalse( new_server.has_privileges(MINIMUM_PRIVILEGES) ) # Check if jeffrey (temporary user) has the appropriate privileges. # The ALL on a global level was granted. server.exec_stmt("GRANT ALL ON *.* TO 'jeffrey'@'%%'") server.exec_stmt("FLUSH PRIVILEGES") self.assertTrue( new_server.has_privileges(MINIMUM_PRIVILEGES) ) # Drop temporary user. server.exec_stmt("DROP USER 'jeffrey'@'%%'") server.set_session_binlog(True) server.disconnect() new_server.disconnect() ConnectionPool().purge_connections(_uuid.UUID(uuid)) def test_max_connections(self): uuid = MySQLServer.discover_uuid(OPTIONS["address"]) server = MySQLServer( _uuid.UUID(uuid), OPTIONS["address"], ) server.connect() res = server.get_variable("max_connections") self.assertNotEqual(int(res), 0)
def test_privileges(self): """Test whether user's have the appropriate privileges. """ # Some privileges MINIMUM_PRIVILEGES = [ "REPLICATION SLAVE", "REPLICATION CLIENT", "SUPER", "SHOW DATABASES", "RELOAD" ] # Connect to server as root and create temporary user. uuid = MySQLServer.discover_uuid(OPTIONS["address"]) server = MySQLServer( _uuid.UUID(uuid), OPTIONS["address"], tests.utils.MySQLInstances().root_user, tests.utils.MySQLInstances().root_passwd ) ConnectionPool().purge_connections(_uuid.UUID(uuid)) server.connect() server.set_session_binlog(False) server.exec_stmt( "CREATE USER 'jeffrey'@'%%' IDENTIFIED BY 'mypass'" ) # Check if jeffrey (temporary user) has the appropriate privileges. # There is not privilege associate to jeffrey. new_server = MySQLServer( _uuid.UUID(uuid), OPTIONS["address"], "jeffrey", "mypass" ) new_server.connect() self.assertFalse( new_server.has_privileges(MINIMUM_PRIVILEGES) ) # Check if jeffrey (temporary user) has the appropriate privileges. # Grant required privileges except RELOAD # There is no RELOAD on a global level. privileges=", ".join([priv for priv in MINIMUM_PRIVILEGES if priv != "RELOAD"] ) server.exec_stmt( "GRANT {privileges} ON *.* TO 'jeffrey'@'%%'".format( privileges=privileges) ) server.exec_stmt("FLUSH PRIVILEGES") self.assertFalse( new_server.has_privileges(MINIMUM_PRIVILEGES) ) # Check if jeffrey (temporary user) has the appropriate privileges. # The RELOAD on a global level was granted. server.exec_stmt("GRANT RELOAD ON *.* TO 'jeffrey'@'%%'") server.exec_stmt("FLUSH PRIVILEGES") self.assertTrue( new_server.has_privileges(MINIMUM_PRIVILEGES) ) # Check if jeffrey (temporary user) has the appropriate privileges. # Revoke privilegs from temporary user. # There is no ALL on a global level. server.exec_stmt("REVOKE ALL PRIVILEGES, GRANT OPTION FROM " "'jeffrey'@'%%'" ) server.exec_stmt("GRANT ALL ON fabric.* TO 'jeffrey'@'%%'") server.exec_stmt("FLUSH PRIVILEGES") self.assertFalse( new_server.has_privileges(MINIMUM_PRIVILEGES) ) # Check if jeffrey (temporary user) has the appropriate privileges. # The ALL on a global level was granted. server.exec_stmt("GRANT ALL ON *.* TO 'jeffrey'@'%%'") server.exec_stmt("FLUSH PRIVILEGES") self.assertTrue( new_server.has_privileges(MINIMUM_PRIVILEGES) ) # Drop temporary user. server.exec_stmt("DROP USER 'jeffrey'@'%%'") server.set_session_binlog(True) server.disconnect() new_server.disconnect() ConnectionPool().purge_connections(_uuid.UUID(uuid))
class TestMySQLSlave(tests.utils.TestCase): """Unit test for the configuration file handling. """ def setUp(self): """Configure the existing environment """ uuid = MySQLServer.discover_uuid(OPTIONS_MASTER["address"]) OPTIONS_MASTER["uuid"] = _uuid.UUID(uuid) self.master = MySQLServer(**OPTIONS_MASTER) self.master.connect() reset_master(self.master) uuid = MySQLServer.discover_uuid(OPTIONS_SLAVE["address"]) OPTIONS_SLAVE["uuid"] = _uuid.UUID(uuid) self.slave = MySQLServer(**OPTIONS_SLAVE) self.slave.connect() stop_slave(self.slave, wait=True) reset_master(self.slave) reset_slave(self.slave, clean=True) def tearDown(self): """Clean up the existing environment """ cleanup_environment() stop_slave(self.slave, wait=True) self.slave.disconnect() self.master.disconnect() def test_switch_master_(self): """Test the switch_master() function. """ # Note this is only being tested with gtids. # Set up replication. master = self.master slave = self.slave # Check that is slave is not connected to any master. self.assertFalse(is_slave_thread_running(slave, (IO_THREAD, ))) self.assertNotEqual(slave_has_master(slave), str(master.uuid)) # Switch to a master. switch_master(slave, master, MySQLInstances().user, MySQLInstances().passwd) start_slave(slave, wait=True) self.assertTrue(is_slave_thread_running(slave, (IO_THREAD, ))) # The IO_THREAD status and the UUID are not atomically updated # for that reason master_uuid can be None. master_uuid = slave_has_master(slave) self.assertTrue( master_uuid == None or master_uuid == str(master.uuid) ) # It is not possible to switch when replication is running. self.assertRaises(_errors.DatabaseError, switch_master, slave, master, MySQLInstances().user, MySQLInstances().passwd ) # Reset and try to reconnect master and slave. stop_slave(slave, wait=True) reset_slave(slave, clean=True) switch_master(slave, master, MySQLInstances().user, MySQLInstances().passwd ) start_slave(slave, wait=True) self.assertTrue(is_slave_thread_running(slave, (IO_THREAD, ))) self.assertEqual(slave_has_master(slave), str(master.uuid)) # Change master's password, reset and try to reconnect master # and slave. stop_slave(slave, wait=True) master.set_session_binlog(False) master.exec_stmt( "SET PASSWORD = PASSWORD('foobar')" ) master.set_session_binlog(True) switch_master(slave, master, MySQLInstances().user, "foobar") start_slave(slave, wait=True) self.assertTrue(is_slave_thread_running(slave, (IO_THREAD, ))) self.assertEqual(slave_has_master(slave), str(master.uuid)) # Reset master's password, reset and try to reconnect master # and slave. stop_slave(slave, wait=True) master.set_session_binlog(False) master.exec_stmt( "SET PASSWORD = PASSWORD('{passwd}')".format( passwd=MySQLInstances().passwd or "") ) master.set_session_binlog(True) switch_master(slave, master, MySQLInstances().user, MySQLInstances().passwd ) start_slave(slave, wait=True) self.assertTrue(is_slave_thread_running(slave, (IO_THREAD, ))) self.assertEqual(slave_has_master(slave), str(master.uuid)) def test_start_stop(self): """Test start/stop slave functions. """ # Set up replication. master = self.master slave = self.slave switch_master(slave, master, MySQLInstances().user, MySQLInstances().passwd ) # Start SQL Thread. start_slave(slave, wait=True, threads=(SQL_THREAD, )) status = get_slave_status(slave) self.assertEqual(status[0].Slave_IO_Running.upper(), "NO") self.assertEqual(status[0].Slave_SQL_Running.upper(), "YES") # Start IO Thread. start_slave(slave, wait=True, threads=(IO_THREAD, )) status = get_slave_status(slave) self.assertEqual(status[0].Slave_IO_Running.upper(), "YES") self.assertEqual(status[0].Slave_SQL_Running.upper(), "YES") # Stop IO Thread stop_slave(slave, wait=True, threads=(IO_THREAD, )) status = get_slave_status(slave) self.assertEqual(status[0].Slave_IO_Running.upper(), "NO") self.assertEqual(status[0].Slave_SQL_Running.upper(), "YES") # Stop IO Thread stop_slave(slave, wait=True, threads=(SQL_THREAD, )) status = get_slave_status(slave) self.assertEqual(status[0].Slave_IO_Running.upper(), "NO") self.assertEqual(status[0].Slave_SQL_Running.upper(), "NO") def test_wait_for_slave(self): """Test wait_for_slave_thread function. """ # Set up replication. master = self.master slave = self.slave switch_master(slave, master, MySQLInstances().user, MySQLInstances().passwd ) # Wait for SQL Thread and IO Thread to stop. This times out. start_slave(slave, wait=True) self.assertRaises(_errors.TimeoutError, wait_for_slave_thread, slave, timeout=1, wait_for_running=False) # Wait for SQL Thread and IO Thread to start. This times out. stop_slave(slave, wait=True) self.assertRaises(_errors.TimeoutError, wait_for_slave_thread, slave, timeout=1, wait_for_running=True) master.exec_stmt("CREATE DATABASE IF NOT EXISTS test") master.exec_stmt("USE test") master.exec_stmt("DROP TABLE IF EXISTS test") master.exec_stmt("CREATE TABLE test(id INTEGER)") binlog = get_master_status(master) binlog_file = binlog[0][0] binlog_pos = binlog[0][1] # Wait until the slave catches up. It returns False because # the threads are stopped. self.assertRaises(_errors.DatabaseError, wait_for_slave, slave, binlog_file, binlog_pos, timeout=0) # Wait until the slave catches up. It returns True because # the threads are running. start_slave(slave, wait=True) wait_for_slave(slave, binlog_file, binlog_pos, timeout=0) # This times out because there are no new events. self.assertRaises(_errors.TimeoutError, wait_for_slave, slave, binlog_file, 2 * binlog_pos, timeout=3) stop_slave(slave, wait=True) master.exec_stmt("DROP TABLE IF EXISTS test") master.exec_stmt("CREATE TABLE test(id INTEGER)") # It throws an error because the threads are stopped. self.assertRaises(_errors.DatabaseError, sync_slave_with_master, slave, master, timeout=0) # Wait until the slave catches up. It does not throw an error # because the threads are running. start_slave(slave, wait=True) sync_slave_with_master(slave, master, timeout=0) # This times out because there are no new events. gtid_executed = "%s:%s" % (str(master.uuid), "1-20") self.assertRaises(_errors.TimeoutError, wait_for_slave_gtid, slave, gtid_executed, timeout=3) def test_check_rpl_health(self): """Test check_slave_issues() function. """ # Set up replication. master = self.master slave = self.slave slave.disconnect() # Try to check the health when one cannot connect to the server. error, ret = check_slave_issues(slave) self.assertEqual(error, True) self.assertEqual(ret['is_not_running'], True) # Try to check the health when change master has not been executed. slave.connect() error, ret = check_slave_issues(slave) self.assertEqual(error, True) self.assertEqual(ret['is_not_configured'], True) # Try to check the health after executing change master. switch_master(slave, master, MySQLInstances().user, MySQLInstances().passwd ) error, ret = check_slave_issues(slave) self.assertEqual(error, True) self.assertEqual(ret['io_not_running'], True) self.assertEqual(ret['sql_not_running'], True) # Try to check the health after starting one thread. start_slave(slave, wait=True, threads=(SQL_THREAD, )) error, ret = check_slave_issues(slave) self.assertEqual(error, True) self.assertEqual(ret['io_not_running'], True) # Create data and synchronize to show there is no gtid behind. master.exec_stmt("CREATE DATABASE IF NOT EXISTS test") master.exec_stmt("USE test") master.exec_stmt("DROP TABLE IF EXISTS test") master.exec_stmt("CREATE TABLE test(id INTEGER)") start_slave(slave, wait=True, threads=(IO_THREAD, )) sync_slave_with_master(slave, master, timeout=0) result = check_slave_delay(slave, master) expected_result = { 'seconds_behind': 0, 'is_not_configured': False, 'gtids_behind': 0, 'is_not_running': False, 'sql_delay': 0 } self.assertEqual(result, expected_result) def test_get_gtid_behind(self): """Test get_gtid_behind() function. """ # Set up replication. master = self.master slave = self.slave switch_master(slave, master, MySQLInstances().user, MySQLInstances().passwd ) # Check gtid that has no information on server_uuid. self.assertRaises(_errors.ProgrammingError, get_num_gtid, "1") sid_1 = "80139491-08ed-11e2-b7bd-f0def124dcc5" sid_2 = "99939491-08ed-11e2-b7bd-f0def124dcc5" # Check the pattern sid:trx_id. ret = get_num_gtid("%s:%s" % (sid_1, "5")) self.assertEqual(ret, 1) # Check the pattern sid:trx_id-trx_id. ret = get_num_gtid("%s:%s" % (sid_1, "5-10")) self.assertEqual(ret, 6) # Check the pattern sid:trx_id-trx_id, trx_id, trx_id-trx-id. ret = get_num_gtid("%s:%s,%s,%s" % (sid_1, "5-10", "20", "25-30")) self.assertEqual(ret, 13) # Check the pattern sid:trx_id-trx_id, sid:trx_id-trx-id. ret = get_num_gtid("%s:%s,%s:%s" % (sid_1, "5-10", sid_2, "5-6")) self.assertEqual(ret, 8) # Check the pattern sid:trx_id-trx_id, sid:trx_id-trx-id but filtering # server_uuids that are different from sid_2. ret = get_num_gtid("%s:%s,%s:%s" % (sid_1, "5-10", sid_2, "5-6"), sid_2) self.assertEqual(ret, 2) # Check empty master_gtid_status and empty slave_gtid_status. master_gtid_status = master.get_gtid_status() slave_gtid_status = slave.get_gtid_status() ret = get_slave_num_gtid_behind(slave, master_gtid_status) self.assertEqual(ret, 0) self.assertEqual(slave_gtid_status[0].GTID_EXECUTED, "") self.assertEqual(master_gtid_status[0].GTID_EXECUTED, "") # It is not possible to do any comparison if the master_gtid_status # is empty. slave.exec_stmt("CREATE DATABASE IF NOT EXISTS test") slave.exec_stmt("USE test") slave.exec_stmt("DROP TABLE IF EXISTS test") master_gtid_status = master.get_gtid_status() slave_gtid_status = slave.get_gtid_status() self.assertRaises(_errors.InvalidGtidError, get_slave_num_gtid_behind, slave, master_gtid_status) self.assertNotEqual(slave_gtid_status[0].GTID_EXECUTED, "") self.assertEqual(master_gtid_status[0].GTID_EXECUTED, "") # Check what happens if there are different sets of transactions. master.exec_stmt("CREATE DATABASE IF NOT EXISTS test") master_gtid_status = master.get_gtid_status() slave_gtid_status = slave.get_gtid_status() ret = get_slave_num_gtid_behind(slave, master_gtid_status) self.assertEqual(ret, 1) self.assertNotEqual(slave_gtid_status[0].GTID_EXECUTED, "") self.assertNotEqual(master_gtid_status[0].GTID_EXECUTED, "") # Check what happens if the slave_gtid_status is empty. reset_master(slave) master_gtid_status = master.get_gtid_status() slave_gtid_status = slave.get_gtid_status() ret = get_slave_num_gtid_behind(slave, master_gtid_status) self.assertEqual(ret, 1) self.assertEqual(slave_gtid_status[0].GTID_EXECUTED, "") self.assertNotEqual(master_gtid_status[0].GTID_EXECUTED, "")
class TestMySQLSlave(unittest.TestCase): """Unit test for the configuration file handling. """ def setUp(self): """Configure the existing environment """ uuid = MySQLServer.discover_uuid(OPTIONS_MASTER["address"]) OPTIONS_MASTER["uuid"] = _uuid.UUID(uuid) self.master = MySQLServer(**OPTIONS_MASTER) self.master.connect() reset_master(self.master) uuid = MySQLServer.discover_uuid(OPTIONS_SLAVE["address"]) OPTIONS_SLAVE["uuid"] = _uuid.UUID(uuid) self.slave = MySQLServer(**OPTIONS_SLAVE) self.slave.connect() stop_slave(self.slave, wait=True) reset_master(self.slave) reset_slave(self.slave, clean=True) def tearDown(self): """Clean up the existing environment """ cleanup_environment() stop_slave(self.slave, wait=True) self.slave.disconnect() self.master.disconnect() def test_switch_master_(self): """Test the switch_master() function. """ # Note this is only being tested with gtids. # Set up replication. master = self.master slave = self.slave # Check that is slave is not connected to any master. self.assertFalse(is_slave_thread_running(slave, (IO_THREAD, ))) self.assertNotEqual(slave_has_master(slave), str(master.uuid)) # Switch to a master. switch_master(slave, master, MySQLInstances().user, MySQLInstances().passwd) start_slave(slave, wait=True) self.assertTrue(is_slave_thread_running(slave, (IO_THREAD, ))) # The IO_THREAD status and the UUID are not atomically updated # for that reason master_uuid can be None. master_uuid = slave_has_master(slave) self.assertTrue( master_uuid == None or master_uuid == str(master.uuid) ) # It is not possible to switch when replication is running. self.assertRaises(_errors.DatabaseError, switch_master, slave, master, MySQLInstances().user, MySQLInstances().passwd ) # Reset and try to reconnect master and slave. stop_slave(slave, wait=True) reset_slave(slave, clean=True) switch_master(slave, master, MySQLInstances().user, MySQLInstances().passwd ) start_slave(slave, wait=True) self.assertTrue(is_slave_thread_running(slave, (IO_THREAD, ))) self.assertEqual(slave_has_master(slave), str(master.uuid)) # Change master's password, reset and try to reconnect master # and slave. stop_slave(slave, wait=True) master.set_session_binlog(False) master.exec_stmt( "SET PASSWORD FOR '{user}'@'%%' = PASSWORD('foobar')".format( user=MySQLInstances().user) ) master.exec_stmt("FLUSH PRIVILEGES") master.set_session_binlog(True) switch_master(slave, master, MySQLInstances().user, "foobar") start_slave(slave, wait=True) self.assertTrue(is_slave_thread_running(slave, (IO_THREAD, ))) self.assertEqual(slave_has_master(slave), str(master.uuid)) # Reset master's password, reset and try to reconnect master # and slave. stop_slave(slave, wait=True) master.set_session_binlog(False) master.exec_stmt( "SET PASSWORD FOR '{user}'@'%%' = " "PASSWORD('{passwd}')".format(user=MySQLInstances().user, passwd=MySQLInstances().passwd or "") ) master.exec_stmt("FLUSH PRIVILEGES") master.set_session_binlog(True) switch_master(slave, master, MySQLInstances().user, MySQLInstances().passwd ) start_slave(slave, wait=True) self.assertTrue(is_slave_thread_running(slave, (IO_THREAD, ))) self.assertEqual(slave_has_master(slave), str(master.uuid)) def test_start_stop(self): """Test start/stop slave functions. """ # Set up replication. master = self.master slave = self.slave switch_master(slave, master, MySQLInstances().user, MySQLInstances().passwd ) # Start SQL Thread. start_slave(slave, wait=True, threads=(SQL_THREAD, )) status = get_slave_status(slave) self.assertEqual(status[0].Slave_IO_Running.upper(), "NO") self.assertEqual(status[0].Slave_SQL_Running.upper(), "YES") # Start IO Thread. start_slave(slave, wait=True, threads=(IO_THREAD, )) status = get_slave_status(slave) self.assertEqual(status[0].Slave_IO_Running.upper(), "YES") self.assertEqual(status[0].Slave_SQL_Running.upper(), "YES") # Stop IO Thread stop_slave(slave, wait=True, threads=(IO_THREAD, )) status = get_slave_status(slave) self.assertEqual(status[0].Slave_IO_Running.upper(), "NO") self.assertEqual(status[0].Slave_SQL_Running.upper(), "YES") # Stop IO Thread stop_slave(slave, wait=True, threads=(SQL_THREAD, )) status = get_slave_status(slave) self.assertEqual(status[0].Slave_IO_Running.upper(), "NO") self.assertEqual(status[0].Slave_SQL_Running.upper(), "NO") def test_wait_for_slave(self): """Test wait_for_slave_thread function. """ # Set up replication. master = self.master slave = self.slave switch_master(slave, master, MySQLInstances().user, MySQLInstances().passwd ) # Wait for SQL Thread and IO Thread to stop. This times out. start_slave(slave, wait=True) self.assertRaises(_errors.TimeoutError, wait_for_slave_thread, slave, timeout=1, wait_for_running=False) # Wait for SQL Thread and IO Thread to start. This times out. stop_slave(slave, wait=True) self.assertRaises(_errors.TimeoutError, wait_for_slave_thread, slave, timeout=1, wait_for_running=True) master.exec_stmt("CREATE DATABASE IF NOT EXISTS test") master.exec_stmt("USE test") master.exec_stmt("DROP TABLE IF EXISTS test") master.exec_stmt("CREATE TABLE test(id INTEGER)") binlog = get_master_status(master) binlog_file = binlog[0][0] binlog_pos = int(binlog[0][1]) # Wait until the slave catches up. It returns False because # the threads are stopped. self.assertRaises(_errors.DatabaseError, wait_for_slave, slave, binlog_file, binlog_pos, timeout=0) # Wait until the slave catches up. It returns True because # the threads are running. start_slave(slave, wait=True) wait_for_slave(slave, binlog_file, binlog_pos, timeout=0) # This times out because there are no new events. self.assertRaises(_errors.TimeoutError, wait_for_slave, slave, binlog_file, 2 * binlog_pos, timeout=3) stop_slave(slave, wait=True) master.exec_stmt("DROP TABLE IF EXISTS test") master.exec_stmt("CREATE TABLE test(id INTEGER)") # It throws an error because the threads are stopped. self.assertRaises(_errors.DatabaseError, sync_slave_with_master, slave, master, timeout=0) # Wait until the slave catches up. It does not throw an error # because the threads are running. start_slave(slave, wait=True) sync_slave_with_master(slave, master, timeout=0) # This times out because there are no new events. gtid_executed = "%s:%s" % (str(master.uuid), "1-20") self.assertRaises(_errors.TimeoutError, wait_for_slave_gtid, slave, gtid_executed, timeout=3) def test_check_rpl_health(self): """Test check_slave_issues() function. """ # Set up replication. master = self.master slave = self.slave slave.disconnect() # Try to check the health when one cannot connect to the server. ret = check_slave_issues(slave) self.assertEqual(ret, {'is_running': False}) # Try to check the health when change master has not been executed. slave.connect() ret = check_slave_issues(slave) self.assertEqual(ret, {'is_configured': False}) # Try to check the health after executing change master. switch_master(slave, master, MySQLInstances().user, MySQLInstances().passwd ) ret = check_slave_issues(slave) self.assertEqual(ret, {'io_running': False, 'sql_running': False}) # Try to check the health after starting one thread. start_slave(slave, wait=True, threads=(SQL_THREAD, )) ret = check_slave_issues(slave) self.assertEqual(ret, {'io_running': False}) # Create data and synchronize to show there is no gtid behind. master.exec_stmt("CREATE DATABASE IF NOT EXISTS test") master.exec_stmt("USE test") master.exec_stmt("DROP TABLE IF EXISTS test") master.exec_stmt("CREATE TABLE test(id INTEGER)") start_slave(slave, wait=True, threads=(IO_THREAD, )) sync_slave_with_master(slave, master, timeout=0) ret = check_slave_delay(slave, master) self.assertEqual(ret, {}) def test_get_gtid_behind(self): """Test get_gtid_behind() function. """ # Set up replication. master = self.master slave = self.slave switch_master(slave, master, MySQLInstances().user, MySQLInstances().passwd ) # Check gtid that has no information on server_uuid. self.assertRaises(_errors.ProgrammingError, get_num_gtid, "1") sid_1 = "80139491-08ed-11e2-b7bd-f0def124dcc5" sid_2 = "99939491-08ed-11e2-b7bd-f0def124dcc5" # Check the pattern sid:trx_id. ret = get_num_gtid("%s:%s" % (sid_1, "5")) self.assertEqual(ret, 1) # Check the pattern sid:trx_id-trx_id. ret = get_num_gtid("%s:%s" % (sid_1, "5-10")) self.assertEqual(ret, 6) # Check the pattern sid:trx_id-trx_id, trx_id, trx_id-trx-id. ret = get_num_gtid("%s:%s,%s,%s" % (sid_1, "5-10", "20", "25-30")) self.assertEqual(ret, 13) # Check the pattern sid:trx_id-trx_id, sid:trx_id-trx-id. ret = get_num_gtid("%s:%s,%s:%s" % (sid_1, "5-10", sid_2, "5-6")) self.assertEqual(ret, 8) # Check the pattern sid:trx_id-trx_id, sid:trx_id-trx-id but filtering # server_uuids that are different from sid_2. ret = get_num_gtid("%s:%s,%s:%s" % (sid_1, "5-10", sid_2, "5-6"), sid_2) self.assertEqual(ret, 2) # Check empty master_gtid_status and empty slave_gtid_status. master_gtid_status = master.get_gtid_status() slave_gtid_status = slave.get_gtid_status() ret = get_slave_num_gtid_behind(slave, master_gtid_status) self.assertEqual(ret, 0) self.assertEqual(slave_gtid_status[0].GTID_EXECUTED, "") self.assertEqual(master_gtid_status[0].GTID_EXECUTED, "") # It is not possible to do any comparison if the master_gtid_status # is empty. slave.exec_stmt("CREATE DATABASE IF NOT EXISTS test") slave.exec_stmt("USE test") slave.exec_stmt("DROP TABLE IF EXISTS test") master_gtid_status = master.get_gtid_status() slave_gtid_status = slave.get_gtid_status() self.assertRaises(_errors.InvalidGtidError, get_slave_num_gtid_behind, slave, master_gtid_status) self.assertNotEqual(slave_gtid_status[0].GTID_EXECUTED, "") self.assertEqual(master_gtid_status[0].GTID_EXECUTED, "") # Check what happens if there are different sets of transactions. master.exec_stmt("CREATE DATABASE IF NOT EXISTS test") master_gtid_status = master.get_gtid_status() slave_gtid_status = slave.get_gtid_status() ret = get_slave_num_gtid_behind(slave, master_gtid_status) self.assertEqual(ret, 1) self.assertNotEqual(slave_gtid_status[0].GTID_EXECUTED, "") self.assertNotEqual(master_gtid_status[0].GTID_EXECUTED, "") # Check what happens if the slave_gtid_status is empty. reset_master(slave) master_gtid_status = master.get_gtid_status() slave_gtid_status = slave.get_gtid_status() ret = get_slave_num_gtid_behind(slave, master_gtid_status) self.assertEqual(ret, 1) self.assertEqual(slave_gtid_status[0].GTID_EXECUTED, "") self.assertNotEqual(master_gtid_status[0].GTID_EXECUTED, "")
class TestErrorLog(unittest.TestCase): """Unit test for testing ErrorLog. """ def setUp(self): """Configure the existing environment """ uuid = MySQLServer.discover_uuid( tests.utils.MySQLInstances().get_address(0)) self.server = MySQLServer(_uuid.UUID(uuid), tests.utils.MySQLInstances().get_address(0)) MySQLServer.add(self.server) def tearDown(self): """Clean up the existing environment """ tests.utils.cleanup_environment() self.server.disconnect() MySQLServer.remove(self.server) def test_init(self): """Test basic properties/methods in the ErrorLog. """ # Check that the input parameters are not changed. interval = get_time_delta(1) now = get_time() input_whens = [30, 40] input_reporters = ["reporter", "reporter"] st = ErrorLog(self.server, interval, now, input_whens, input_reporters) self.assertEqual(st.server_uuid, self.server.uuid) self.assertEqual(st.interval, interval) self.assertEqual(st.now, now) self.assertEqual(st.whens, input_whens) self.assertEqual(st.reporters, input_reporters) # If whens and reporters don't have the same length, an exception is # raised interval = get_time_delta(1) now = get_time() input_whens = [0, 0, 0, 0] input_reporters = [] self.assertRaises(AssertionError, ErrorLog, self.server, interval, now, input_whens, input_reporters) def test_persistence(self): """Test ErrorLog. """ # Update/Notify and fetch, they should match. interval = get_time_delta(1) now = get_time() input_whens = [now, now] input_reporters = ["client:1000", "client:2000"] st = ErrorLog(self.server, interval, now, input_whens, input_reporters) ErrorLog.add(self.server, now, "client:1000", "error") ErrorLog.add(self.server, now, "client:2000", "error") new_st = ErrorLog.fetch(self.server, interval, now) self.assertEqual(st.reporters, new_st.reporters) self.assertEqual(st.whens, new_st.whens) # Call remove, they should be empty and match. interval = get_time_delta(1) now = get_time() input_whens = [] input_reporters = [] ErrorLog.remove(self.server) st = ErrorLog(self.server, interval, now, input_whens, input_reporters) new_st = ErrorLog.fetch(self.server, interval, now) self.assertEqual(st.reporters, new_st.reporters) self.assertEqual(st.whens, new_st.whens) # Update/Notify and refresh, they should match. interval = get_time_delta(10) now = get_time() input_whens = [now, now - get_time_delta(5)] input_reporters = ["client:1000", "client:2000"] st = ErrorLog(self.server, interval, now, [], []) ErrorLog.add(self.server, now, "client:1000", "error") ErrorLog.add(self.server, now - get_time_delta(5), "client:2000", "error") ErrorLog.add(self.server, now - get_time_delta(11), "client:3000", "error") st.refresh() self.assertEqual(set(st.reporters), set(input_reporters)) self.assertEqual(set(st.whens), set(input_whens)) # Check whether a statement similar to the one used in the # event is fine. ErrorLog.remove(self.server) ErrorLog.add(self.server, now, "client:1000", "error") ErrorLog.add(self.server, now, "client:2000", "error") persister = _persistence.current_persister() out = persister.exec_stmt( "SELECT reported, UTC_TIMESTAMP() as now, " "TIMEDIFF(UTC_TIMESTAMP(), reported - MAKETIME(2,0,0)) as diff " "FROM error_log") _LOGGER.debug("Output test persistence %s.", out) self.assertEqual(len(out), 2) res = persister.exec_stmt( "DELETE FROM error_log WHERE " "TIMEDIFF(UTC_TIMESTAMP(), reported - MAKETIME(2,0,0)) > " "MAKETIME(1,0,0)") _LOGGER.debug("Output test persistence %s.", res) out = persister.exec_stmt( "SELECT reported, UTC_TIMESTAMP() as now, " "TIMEDIFF(UTC_TIMESTAMP(), reported - MAKETIME(2,0,0)) as diff " "FROM error_log") _LOGGER.debug("Output test persistence %s.", out) self.assertEqual(len(out), 0) def test_check_instability(self): """Test whether a server can be considered unstable or not. """ # Update/Notify and refresh, they should match. interval = get_time_delta(10) now = get_time() input_whens = [now, now - get_time_delta(5)] input_reporters = ["client:1000", "client:2000"] st = ErrorLog(self.server, interval, now, [], []) ErrorLog.add(self.server, now, "client:1000", "error") ErrorLog.add(self.server, now - get_time_delta(5), "client:2000", "error") ErrorLog.add(self.server, now - get_time_delta(11), "client:3000", "error") st.refresh() self.assertEqual( st.is_unstable(n_notifications=1, n_reporters=1, filter_reporter=None), True) self.assertEqual( st.is_unstable(n_notifications=2, n_reporters=2, filter_reporter=None), True) self.assertEqual( st.is_unstable(n_notifications=3, n_reporters=2, filter_reporter=None), False) self.assertEqual( st.is_unstable(n_notifications=2, n_reporters=3, filter_reporter=None), False) self.assertEqual( st.is_unstable(n_notifications=1, n_reporters=1, filter_reporter=["client:2000"]), True)