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