예제 #1
0
    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))
예제 #2
0
 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")
예제 #4
0
    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")
예제 #5
0
 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'))
예제 #6
0
    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.")
예제 #7
0
 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")
예제 #8
0
    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', ))
예제 #9
0
    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)
예제 #10
0
 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()
예제 #11
0
    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()
예제 #12
0
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")