def test_server_str(self): """ Test str(server) function. """ # Test without using unix socket. opt_dict = { "conn_info": { 'user': '******', 'host': 'myhost', 'port': 3306 } } myserver = server.Server(opt_dict) self.assertEqual("{0}".format(str(myserver)), "'{0}@{1}'".format(myserver.host, myserver.port)) # Test using unix socket (platform dependent, only for POSIX). if os.name == "posix": opt_dict = { "conn_info": { 'user': '******', 'host': 'myhost', 'port': 3306, 'unix_socket': 'mysocket' } } myserver = server.Server(opt_dict) self.assertEqual( "{0}".format(str(myserver)), "'{0}@{1}'".format(myserver.host, myserver.socket))
def setUp(self): """ Setup server connections (for all tests) . """ self.source_cnx = {'conn_info': self.options[SERVER_CNX_OPT][0]} self.destination_cnx = {'conn_info': self.options[SERVER_CNX_OPT][1]} self.source = server.Server(self.source_cnx) self.destination = server.Server(self.destination_cnx) self.source.connect() self.destination.connect()
def setUp(self): """ Setup server connections (for all tests) . """ self.source_cnx = {'conn_info': self.options[SERVER_CNX_OPT][0]} self.destination_cnx = {'conn_info': self.options[SERVER_CNX_OPT][1]} self.source = server.Server(self.source_cnx) self.destination = server.Server(self.destination_cnx) self.source.connect() self.destination.connect() try: self.source_gtids_enabled = self.source.supports_gtid() except exceptions.GadgetServerError: self.source_gtids_enabled = False try: self.destination_gtids_enabled = self.destination.supports_gtid() except exceptions.GadgetServerError: self.destination_gtids_enabled = False if self.destination_gtids_enabled: self.destination_has_no_gtid_executed = not \ self.destination.get_gtid_executed(skip_gtid_check=False) else: self.destination_has_no_gtid_executed = False self.mysqldump_exec = get_tool_path(None, "mysqldump", search_path=True, required=False) self.mysql_exec = get_tool_path(None, "mysql", search_path=True, required=False) # Search for mysqldump only on path (ignore MySQL default paths). self.mysqldump_exec_in_defaults = get_tool_path(None, "mysqldump", search_path=False, required=False) # setup source server with a sample database and users data_sql_path = os.path.normpath( os.path.join(__file__, "..", "std_data", "basic_data.sql")) self.source.read_and_exec_sql(data_sql_path, verbose=True) # Create user without super permission on both source and destination self.source.exec_query("create user no_super@'%'") self.source.exec_query("GRANT ALL on *.* to no_super@'%'") self.source.exec_query("REVOKE SUPER on *.* from no_super@'%'") self.destination.exec_query("create user no_super@'%'") self.destination.exec_query("GRANT ALL on *.* to no_super@'%'") self.destination.exec_query("REVOKE SUPER on *.* from no_super@'%'") if self.destination_has_no_gtid_executed: self.destination.exec_query("RESET MASTER")
def test_install_plugin(self): """Test the install_plugin method""" skip_if_not_GR_approved(self.server) if self.server.is_plugin_installed(GR_PLUGIN_NAME): self.server.exec_query("UNINSTALL PLUGIN group_replication") self.server.install_plugin(GR_PLUGIN_NAME) self.assertTrue(self.server.is_plugin_installed(GR_PLUGIN_NAME), "GR plugin was expected to be loaded") self.server.stop_plugin(GR_PLUGIN_NAME) self.assertTrue(self.server.is_plugin_installed(GR_PLUGIN_NAME), "GR plugin was expected to be installed") self.server.uninstall_plugin(GR_PLUGIN_NAME) self.assertFalse(self.server.is_plugin_installed(GR_PLUGIN_NAME, is_active=True)) # Expect the plugin to start due to the missing configuration with self.assertRaises(GadgetQueryError): self.server.start_plugin(GR_PLUGIN_NAME) # 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)) server2 = server.Server(self.server_cnx) server2.connect() # Test re-attempt to plugin uninstall after not been installed again self.server.uninstall_plugin(GR_PLUGIN_NAME) server2.install_plugin(GR_PLUGIN_NAME) self.assertTrue(self.server.is_plugin_installed(GR_PLUGIN_NAME), "GR plugin was expected to be installed")
def setUp(self): """setUp method""" self.maxDiff = None self.server_cnx = {'conn_info': self.options[SERVER_CNX_OPT][0]} self.server = server.Server(self.server_cnx) self.server.connect() self.req_check = RequirementChecker() self.option_file = os.path.normpath( os.path.join(__file__, "..", "std_data", "option_files", 'my.cnf'))
def test_server(self): """Test Server object creation. This test check additional circumstances of the creation of the Server object. Note: Successful creation of the server object already done in the setUp() function and it is required for other tests. """ # Create server object with no options. with self.assertRaises(GadgetCnxInfoError): # options.get("conn_info") cannot be None server.Server(None) # Missing mandatory options for "conn_info" (host, user or port). opt_dict = {"conn_info": {}} with self.assertRaises(GadgetCnxInfoError): server.Server(opt_dict) opt_dict = {"conn_info": {'user': '******', 'port': 3306}} with self.assertRaises(GadgetCnxInfoError): server.Server(opt_dict) opt_dict = {"conn_info": {'host': 'myhost', 'port': 3306}} with self.assertRaises(GadgetCnxInfoError): server.Server(opt_dict) opt_dict = {"conn_info": {'user': '******', 'host': 'myhost'}} with self.assertRaises(GadgetCnxInfoError): server.Server(opt_dict) # No mandatory option missing. opt_dict = { "conn_info": { 'user': '******', 'host': 'myhost', 'port': 3306 } } myserver = server.Server(opt_dict) self.assertIsInstance( myserver, server.Server, "The object 'myserver' is not an instance" "of Server.") # Use SSL options. opt_dict = { "conn_info": { 'user': '******', 'host': 'myhost', 'port': 3306, 'ssl_ca': 'myca', 'ssl_key': 'mykey', 'ssl_cert': 'mycert', 'ssl': True } } myserver = server.Server(opt_dict) self.assertIsInstance( myserver, server.Server, "The object 'myserver' is not an instance" "of Server.")
def test_is_alive_false(self): """ Test is_alive() function after disconnect. """ server2 = server.Server(self.server_cnx) alive = server2.is_alive() self.assertFalse(alive, "The server was expected to be not alive") server2.connect() alive = server2.is_alive() self.assertTrue(alive, "The server was expected to be not alive") server2.disconnect() alive = server2.is_alive() self.assertFalse(alive, "The server was expected to be not alive")
def test_get_connection_dictionary(self): """ Test get_connection_dictionary function. """ # Test when conn_info is None. con_info = server.get_connection_dictionary(None) self.assertIsNone( con_info, "get_connection_dictionary() expected to " "return None") # Use a Server as parameter. # Note: Use fill connection information to ensure all is correctly # obtained from server instance using get_connection_values(). opt_dict = { 'conn_info': { 'user': '******', 'host': 'myhost', 'port': 3306, 'passwd': 'mypasswd', 'unix_socket': 'mysocket', 'ssl_ca': 'myca', 'ssl_cert': 'mycert', 'ssl_key': 'mykey', 'ssl': 'myssl' } } myserver = server.Server(opt_dict) con_info = server.get_connection_dictionary(myserver) self.assertEqual(con_info, opt_dict['conn_info']) # Use a string as parameter. opt_str = 'myuser@myhost:3306' con_info = server.get_connection_dictionary(opt_str) expected = {'user': '******', 'host': 'myhost', 'port': 3306} self.assertEqual(con_info, expected) # Use SSL dict. cnx_dict = {'user': '******', 'host': 'myhost', 'port': 3306} ssl_dict = {'ssl_ca': 'mycertificate'} con_info = server.get_connection_dictionary(cnx_dict, ssl_dict=ssl_dict) expected = cnx_dict expected.update(ssl_dict) self.assertEqual(con_info, expected) # Invalid parameter type (using a tuple). with self.assertRaises(GadgetCnxInfoError): server.get_connection_dictionary(('mytuple', ))
def test_get_version(self): """ Test get_version function. """ # Get version from default server (clear cached version values first). self.server._version = None # pylint: disable=W0212 self.server._version_full = None # pylint: disable=W0212 version = self.server.get_version() self.assertIsInstance(version, list) self.assertEqual(len(version), 3) # Get version again, cached values are returned. version = self.server.get_version() self.assertIsInstance(version, list) self.assertEqual(len(version), 3) version = self.server.get_version(full=True) self.assertIsInstance(version, str) # Use a different Server object to get the full version, otherwise the # previously cached value is returned. cnx_opt = copy.deepcopy(self.server_cnx) myserver = server.Server(cnx_opt) myserver.connect() version = myserver.get_version(full=True) self.assertIsInstance(version, str) # Get version again, cached values are returned. version = myserver.get_version() self.assertIsInstance(version, list) self.assertEqual(len(version), 3) version = myserver.get_version(full=True) self.assertIsInstance(version, str) # Get version from a disconnected server. # Note: clear cached values to force trying to get version from server. myserver.disconnect() myserver._version = None # pylint: disable=W0212 myserver._version_full = None # pylint: disable=W0212 version = myserver.get_version() self.assertIsNone(version)
def setUp(self): """ Setup base server connection (for all tests). """ self.server_cnx = {'conn_info': self.options[SERVER_CNX_OPT][0]} self.server = server.Server(self.server_cnx) self.server.connect()
def test_connect_disconnect(self): """Test connect and disconnect methods. This test check additional use case to try to connect and disconnect to the server. Note: Successful connection to a server is already done in the setUp() function and it is required for other tests. """ # Connect to the server. cnx_opt = copy.deepcopy(self.server_cnx) myserver = server.Server(cnx_opt) myserver.connect() self.assertTrue(myserver.is_alive(), "The myserver was expected to be alive") # Try to connect again. myserver.connect() self.assertTrue(myserver.is_alive(), "The myserver was expected to be alive") # Disconnect from the server. myserver.disconnect() self.assertFalse(myserver.is_alive(), "The myserver was expected to be not alive") # Try to disconnect again. myserver.disconnect() self.assertFalse(myserver.is_alive(), "The myserver was expected to be not alive") # Try to connect using wrong passwd. cnx_opt = copy.deepcopy(self.server_cnx) cnx_opt['conn_info'].update({'passwd': 'wrongpass'}) myserver = server.Server(cnx_opt) with self.assertRaises(GadgetError): myserver.connect() # Try to connect to an invalid server. opt_dict = {"conn_info": {'user': '******', 'host': 'myhost', 'port': 3306, 'unix_socket': 'mysocket'}} myserver = server.Server(opt_dict) with self.assertRaises(GadgetError): myserver.connect() # Try to disconnect from an invalid server. with self.assertRaises(GadgetCnxError): myserver.disconnect() # Try to connect using fake SSL values. cnx_opt_ssl = copy.deepcopy(self.server_cnx) cnx_opt_ssl['conn_info'].update({'ssl_ca': 'myca', 'ssl_key': 'mykey', 'ssl_cert': 'mycert', 'ssl': True}) myserver = server.Server(cnx_opt_ssl) with self.assertRaises(GadgetError): myserver.connect() # Try to connect using previous fake SSL values and ssl_ca set to None. myserver.ssl_ca = None with self.assertRaises(GadgetError): myserver.connect()
def clone_server(connection_dict, adapter_name, image_name=None): """Clone server contents from source to destination. :param connection_dict: dictionary of dictionaries of connection information: mysql users and host users. It can have the following keys: source_mysql, destination_mysql, source_host, destination_host. Each of these keys has as a value a dict with the following keys: user, hostname, port, passwd and their respective values. :type connection_dict: dict :param adapter_name: name of the adapter to be used :type adapter_name: str :param image_name: Name of the image file to be used. If a name is provided cloning via image will be used. However, if its value is None then stream cloning will be used. :type image_name: str """ # Create source and destination connection dictionaries source_dict = connection_dict[adapters.MYSQL_SOURCE] destination_dict = connection_dict[adapters.MYSQL_DEST] # Creating Server instances for source and destination servers try: source = server.Server({'conn_info': source_dict}) except exceptions.GadgetError as err: _LOGGER.error("Unable to create a Server instance for source server." "Source dict was: %s", source_dict) raise err try: destination = server.Server({'conn_info': destination_dict}) except exceptions.GadgetError as err: _LOGGER.error("Unable to create a Server instance for destination " "server. Destination dict was: %s", destination_dict) raise err # Connect to source and destination servers try: source.connect() except exceptions.GadgetServerError as err: raise exceptions.GadgetError("Unable to connect to source server: {0}" "".format(str(err))) try: destination.connect() except exceptions.GadgetServerError as err: raise exceptions.GadgetError("Unable to connect to destination " "server: {0}.".format(str(err))) # we cannot use the same server as both source and destination _LOGGER.debug("Checking if source and destination are the same server") same_server = server.check_hostname_alias(source_dict, destination_dict) if same_server: raise exceptions.GadgetError( "The source and destination servers cannot be the same server.") else: _LOGGER.debug("Source and destination are not the same server.") # import all adapter modules for module in adapters.__all__: # pylint: disable=E1101 _LOGGER.debug("Importing module %s.%s", adapters.__package__, module) importlib.import_module("{0}.{1}".format(adapters.__package__, module)) # Get list of adapters with Validation and check if specified adapter # is one of them adapters_with_validation = tools.get_subclass_dict(adapters.WithValidation) validation_adapter = adapters_with_validation.get(adapter_name, None) if validation_adapter is None: _LOGGER.info("Adapter'%s' doesn't exist or contains no pre-clone and " "post-clone requirement validations to be done.", adapter_name) else: _LOGGER.info("Checking pre-clone requirements of adapter '%s'.", adapter_name) # This method might raise an exception that will be caught by the # mysql-server-clone script validation_adapter.pre_validation(connection_dict) _LOGGER.info("Pre-clone requirement validation passed") # Check if we are using stream or image type of backup if image_name is None: # Using image stream backup, so we need to check if the adapter # specified supports it file_adapters = tools.get_subclass_dict(adapters.StreamClone) adapter = file_adapters.get(adapter_name, None) if adapter is None: raise exceptions.GadgetError( "There is no stream cloning supporting adapter named '{0}'." " Use one of the following values for the --using option: " "{1}.".format(adapter_name, ", ".join(file_adapters.keys()))) # pylint: disable=E1101 _LOGGER.step("Dumping the contents of source server into destination " "server via streaming using adapter '%s'.", adapter_name) adapter.clone_stream(connection_dict) else: image_adapters = tools.get_subclass_dict(adapters.ImageClone) adapter = image_adapters.get(adapter_name, None) if adapter is None: raise exceptions.GadgetError( "There is no image cloning supporting adapter named '{0}'. " " Use one of the following values for the --using option: " "{1}.".format(adapter_name, ", ".join(image_adapters.keys()))) # Check if image file is absolute path or not if os.path.isabs(image_name): image_path = image_name else: # if relative path or filename image_path = os.path.abspath(image_name) # Raise exception if file already exists if os.path.isfile(image_path): raise exceptions.GadgetError( "Cannot create image file at {0}. File already exists." "".format(image_path)) # pylint: disable=E1101 _LOGGER.step("Dumping the contents of source server into destination " "server via image '%s' using adapter '%s'. ", image_path, adapter_name) adapter.backup_to_image(connection_dict, image_path) adapter.move_image(connection_dict, image_path) adapter.restore_from_image(connection_dict, image_path) if validation_adapter is not None: _LOGGER.info("Checking post-clone requirements of adapter '%s'.", adapter_name) # This method might raise an exception that will be caught by the # mysql-server-clone script validation_adapter.post_validation(connection_dict) _LOGGER.info("Post-clone requirement validation passed")