Esempio n. 1
0
    def post_validation(connection_dict):
        """ Checks for requirements after clone operation is executed.

        If requirements are not met, the implementation must raise an
        exception and as a result the clone operation will be reported as
        having failed. The message of the exception will be logged as the cause
        of the clone operation having not met post-clone requirements.
        param connection_dict: dictionary of dictionaries of connection
                               information: mysql users and host users. It can
                               have the following keys: MYSQL_SOURCE,
                               MYSQL_DEST, HOST_SOURCE and HOST_DEST.
                               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
        :raises: Exception if post-clone requirements are not met.

        Note: This method is only executed if clone operation occurs without
        any errors.
        """
        # Creating Server instances for source and destination servers
        source_dict = connection_dict[MYSQL_SOURCE]
        destination_dict = connection_dict[MYSQL_DEST]
        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_server.connect()
        except exceptions.GadgetServerError as err:
            raise exceptions.GadgetError(
                "Unable to connect to source server: {0}".format(str(err)))
        try:
            # try to connect with original destination user credentials
            destination_server.connect()
        except exceptions.GadgetServerError as dest_err:
            # if failed, try to use source user credentials
            orig_dest_user = destination_dict["user"]
            destination_dict["user"] = source_dict["user"]
            destination_dict["passwd"] = source_dict["passwd"]
            try:
                destination_server = Server({'conn_info': destination_dict})
            except exceptions.GadgetError as e:
                _LOGGER.error(
                    "Unable to create a Server instance for destination "
                    "server. Destination dict was: %s", destination_dict)
                raise e
            try:
                destination_server.connect()
            except exceptions.GadgetServerError as source_err:
                raise exceptions.GadgetError(
                    "Unable to connect to destination server after using both "
                    "destination user '{0}' and source user '{1}' "
                    "credentials. Error for destination user: '******'. Error "
                    "for source user: '******'."
                    "".format(orig_dest_user, source_server.user,
                              str(dest_err), str(source_err)))

        # if GTIDs are enabled we must check that GTID executed set is the same
        # for both servers.
        try:
            source_gtid_executed = source_server.get_gtid_executed(
                skip_gtid_check=False)
        except exceptions.GadgetError:
            source_gtid_executed = ""  # if GTIDs are disabled assume empty set
        try:
            dest_gtid_executed = destination_server.get_gtid_executed(
                skip_gtid_check=False)
        except exceptions.GadgetError:
            dest_gtid_executed = ""  # if GTIDs are disabled assume empty set
        if not source_gtid_executed == dest_gtid_executed:
            raise exceptions.GadgetError(
                "Cloning post-condition check failed. Source and destination "
                "servers don't have the same GTID_EXECUTED value.")
Esempio n. 2
0
    def pre_validation(connection_dict):
        """ Checks for requirements before clone operation is executed.

        If requirements are not met, the implementation must raise an exception
        and as a result the clone operation will be cancelled before it starts.
        The message of the exception will be logged as the cause of the clone
        operation having not met the pre-clone requirements.
        param connection_dict: dictionary of dictionaries of connection
                               information: mysql users and host users. It can
                               have the following keys: MYSQL_SOURCE,
                               MYSQL_DEST, HOST_SOURCE and HOST_DEST.
                               Each of these keys has as a value a dict with
                               the following keys: user, hostname, port,
                               passwd and their respective values.
        :raises: Exception if pre-clone requirements are not met.
        """
        # Creating Server instances for source and destination servers
        source_dict = connection_dict[MYSQL_SOURCE]
        destination_dict = connection_dict[MYSQL_DEST]
        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_server.connect()
        except exceptions.GadgetServerError as err:
            raise exceptions.GadgetError(
                "Unable to connect to source server: {0}".format(str(err)))
        try:
            destination_server.connect()
        except exceptions.GadgetServerError as err:
            raise exceptions.GadgetError("Unable to connect to destination "
                                         "server: {0}.".format(str(err)))

        # Check if source server has the same GTID mode as destination server
        _LOGGER.debug("Checking if source server and destination have the "
                      "same GTID mode.")
        try:
            source_has_gtids = source_server.supports_gtid()
        except exceptions.GadgetServerError:
            # if GTIDs are not supported it is the same as having them disabled
            source_has_gtids = False
        try:
            destination_has_gtids = destination_server.supports_gtid()
        except exceptions.GadgetServerError:
            # if GTIDs are not supported it is the same as having them disabled
            destination_has_gtids = False

        if not source_has_gtids == destination_has_gtids:
            raise exceptions.GadgetError(
                "Cloning pre-condition check failed: Source and destination "
                "servers must have the same GTID mode.")
        if destination_has_gtids:
            # if destination has GTID support enabled, we must make sure
            # it is empty.
            gtid_executed = destination_server.get_gtid_executed()
            if gtid_executed:
                raise exceptions.GadgetError(
                    "Cloning pre-condition check failed: GTID executed set "
                    "must be empty on destination server.")

        # Check if user has super privilege on source
        # required for the set super_only
        _LOGGER.debug("Checking if MySQL source user has the required "
                      "SUPER privilege.")
        source_username = "******".format(source_server.user,
                                           source_server.host)
        source_user = User(source_server, source_username,
                           source_server.passwd)
        if not source_user.has_privilege('*', '*', 'SUPER'):
            raise exceptions.GadgetError(
                "SUPER privilege is required for the MySQL user '{0}' on "
                "the source server.".format(source_server.user))

        # Check if user has super privilege on destination
        _LOGGER.debug("Checking if MySQL destination user has the "
                      "required SUPER privilege.")
        dest_username = "******".format(destination_server.user,
                                         destination_server.host)
        dest_user = User(destination_server, dest_username,
                         destination_server.passwd)
        if not dest_user.has_privilege('*', '*', 'SUPER'):
            raise exceptions.GadgetError(
                "SUPER privilege is required for the MySQL user '{0}' on "
                "the destination server.".format(destination_server.user))

        # After the clone operation, mysql user table on destination server
        # will be replaced by the mysql user table from source server. So we
        # must make sure that:
        # *) Either source or destination users exist on source server
        #    with a hostname that matches destination server hostname.
        # If this conditions holds, then if clone operation
        # finishes successfully we are sure to be able to connect to the
        # destination server to do any post_clone verification.
        # Otherwise we must issue a warning stating that the post_clone
        # verification might fail.
        if (source_server.user_host_exists(destination_server.user,
                                           destination_server.host)
                or source_server.user_host_exists(source_server.user,
                                                  destination_server.host)):
            return  # Condition holds, no need to issue a warning.
        else:
            _LOGGER.warning(
                "Cloning will replace mysql user table on "
                "destination server with mysql user table from "
                "source. Since neither source user account "
                "'%s' nor destination user account '%s' exist on "
                "source server with a wildcard hostname (%%), the "
                "post clone requirement check might fail because "
                "the tool might not be able to successfully "
                "connect to the destination server.", source_server.user,
                destination_server.user)