Ejemplo n.º 1
0
    def restore_from_image(connection_dict, image_path):
        """"Restore an image file into the destination server.

        :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
        :param image_path: name/path of the image that we will be read to do
                           the restore operation.
        :type image_path:  string
        """
        # Creating Server for destination server
        destination_dict = connection_dict[MYSQL_DEST]
        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 destination server
        try:
            destination_server.connect()
        except exceptions.GadgetServerError as err:
            raise exceptions.GadgetError("Unable to connect to destination "
                                         "server: {0}.".format(str(err)))

        mysqlc_exe = get_tool_path(None,
                                   "mysql",
                                   search_path=True,
                                   required=False)
        if not mysqlc_exe:
            raise exceptions.GadgetError(
                "Could not find MySQL client executable. Make sure it is on "
                "{0}.".format(PATH_ENV_VAR))

        # Create config_file for mysql client
        client_config_file = Server.to_config_file(destination_server,
                                                   "client")
        # Replace image_name backslashes with forward slashes to pass it to the
        # mysql source command
        if os.name == 'nt':
            image_path = '/'.join(image_path.split(os.sep))
        # Create command list to restore the backup
        restore_cmd = shlex.split(
            _MYSQLDUMP_IMAGE_RESTORE_CMD.format(mysqlc_exec=mysqlc_exe,
                                                config_file=client_config_file,
                                                image_file=image_path,
                                                quote=QUOTE_CHAR))
        try:
            _LOGGER.debug(
                "Restoring contents of destination server from "
                "image file %s using command: %s", image_path,
                " ".join(restore_cmd))
            restore_process = subprocess.Popen(restore_cmd,
                                               stderr=subprocess.PIPE,
                                               universal_newlines=True)

            _, err = restore_process.communicate()
            if restore_process.returncode:
                raise exceptions.GadgetError(
                    "MySQL client exited with error code '{0}' and message: "
                    "'{1}'. ".format(restore_process.returncode, err.strip()))
            else:
                _LOGGER.info("Restoring from file was successful.")
        finally:
            # delete created configuration file
            try:
                _LOGGER.debug("Removing configuration file '%s'",
                              client_config_file)
                os.remove(client_config_file)
            except OSError:
                _LOGGER.warning("Unable to remove configuration file '%s'",
                                client_config_file)
            else:
                _LOGGER.debug("Configuration file '%s' successfully removed",
                              client_config_file)

        _LOGGER.info(
            "Destination server contents successfully loaded from "
            "file '%s'.", image_path)
Ejemplo n.º 2
0
    def clone_stream(connection_dict):
        """Clone the contents of source server into destination using a stream.

        :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
        """
        # Check tool requirements
        mysqldump_exe = get_tool_path(None,
                                      "mysqldump",
                                      search_path=True,
                                      required=False)
        if not mysqldump_exe:
            raise exceptions.GadgetError(
                "Could not find mysqldump executable. Make sure it is on "
                "{0}.".format(PATH_ENV_VAR))

        mysqlc_exe = get_tool_path(None,
                                   "mysql",
                                   search_path=True,
                                   required=False)
        if not mysqlc_exe:
            raise exceptions.GadgetError(
                "Could not find mysql client executable. Make sure it is on "
                "{0}.".format(PATH_ENV_VAR))

        # 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)))

        # Create config_file for mysqldump
        dump_config_file = Server.to_config_file(source_server, "mysqldump")

        # Create config_file for mysql client
        client_config_file = Server.to_config_file(destination_server,
                                                   "client")

        # Create command list to create the backup
        backup_cmd = shlex.split(
            _MYSQLDUMP_STREAM_BACKUP_CMD.format(mysqldump_exec=mysqldump_exe,
                                                config_file=dump_config_file,
                                                quote=QUOTE_CHAR))

        # Create command list to restore the backup
        restore_cmd = shlex.split(
            _MYSQLDUMP_STREAM_RESTORE_CMD.format(
                mysqlc_exec=mysqlc_exe,
                config_file=client_config_file,
                quote=QUOTE_CHAR))

        # enable global read_lock
        _LOGGER.debug("Locking global read lock on source server to prevent "
                      "modifications during clone.")
        source_server.toggle_global_read_lock(True)
        _LOGGER.debug("Source server locked (read-only=ON)")

        try:
            _LOGGER.debug(
                "Dumping contents of source server using command: "
                "%s", " ".join(backup_cmd))

            dump_process = subprocess.Popen(backup_cmd,
                                            stdout=subprocess.PIPE,
                                            stderr=subprocess.PIPE,
                                            universal_newlines=True)
            _LOGGER.debug(
                "Restoring contents to destination server using "
                "command: %s", " ".join(restore_cmd))
            restore_process = subprocess.Popen(restore_cmd,
                                               stdin=dump_process.stdout,
                                               stdout=subprocess.PIPE,
                                               stderr=subprocess.PIPE,
                                               universal_newlines=True)
            # We call dump_process.stdout.close before
            # restore_process.communicate so that if restore_process dies
            # prematurely the SIGPIPE signal can be processed by dump_process
            # allowing it to exit.
            dump_process.stdout.close()
            # Wait for restore process to end and get the output.
            _, err = restore_process.communicate()
            dump_process.wait()
            error_msg = ""
            if dump_process.returncode:
                error_msg = (
                    "mysqldump exited with error code '{0}' and message: "
                    "'{1}'. ".format(dump_process.returncode,
                                     dump_process.stderr.read().strip()))
            else:
                _LOGGER.info("Dump process successfully completed.")
            if restore_process.returncode:
                error_msg += (
                    "MySQL client exited with error code '{0}' and message: "
                    "'{1}'".format(restore_process.returncode, err.strip()))
            else:
                _LOGGER.info("Restore process successfully completed.")

            # If there were errors, raise an exception to warn the user.
            if error_msg:
                raise exceptions.GadgetError(error_msg)
        finally:
            # disable global read_lock
            _LOGGER.debug("Unlocking global read lock on source server.")
            source_server.toggle_global_read_lock(False)
            _LOGGER.debug("Source server unlocked. (read-only=OFF)")
            # delete created configuration files
            try:
                _LOGGER.debug("Removing configuration file '%s'",
                              dump_config_file)
                os.remove(dump_config_file)
            except OSError:
                _LOGGER.warning("Unable to remove configuration file '%s'",
                                dump_config_file)
            else:
                _LOGGER.debug("Configuration file '%s' successfully removed",
                              dump_config_file)
            try:
                _LOGGER.debug("Removing configuration file '%s'",
                              client_config_file)
                os.remove(client_config_file)
            except OSError:
                _LOGGER.warning("Unable to remove configuration file '%s'",
                                client_config_file)
            else:
                _LOGGER.debug("Configuration file '%s' successfully removed",
                              client_config_file)

        _LOGGER.info("Contents loaded successfully into destination " "server")
Ejemplo n.º 3
0
    def backup_to_image(connection_dict, image_path):
        """"Backup the contents of source server into an image file.

        :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
        :param image_path: name/path of the image that we will create with the
                           backup.
        :type image_path:  string
        """
        # Check tool requirements
        mysqldump_exe = get_tool_path(None,
                                      "mysqldump",
                                      search_path=True,
                                      required=False)
        if not mysqldump_exe:
            raise exceptions.GadgetError(
                "Could not find mysqldump executable. Make sure it is on "
                "{0}.".format(PATH_ENV_VAR))

        # Creating Server instance for source server
        source_dict = connection_dict[MYSQL_SOURCE]
        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

        # Connect to source server
        try:
            source_server.connect()
        except exceptions.GadgetServerError as err:
            raise exceptions.GadgetError(
                "Unable to connect to source server: {0}".format(str(err)))

        # Create config_file for mysqldump
        dump_config_file = Server.to_config_file(source_server, "mysqldump")

        # Create command list for backup
        backup_cmd = shlex.split(
            _MYSQLDUMP_IMAGE_BACKUP_CMD.format(mysqldump_exec=mysqldump_exe,
                                               config_file=dump_config_file,
                                               image_file=image_path,
                                               quote=QUOTE_CHAR))

        # enable global read_lock
        _LOGGER.debug("Locking global read lock on source server to prevent "
                      "modifications during clone.")
        source_server.toggle_global_read_lock(True)
        _LOGGER.debug("Source server locked (read-only=ON)")

        # Do the backup
        try:
            _LOGGER.debug(
                "Dumping contents of source server to image file %s "
                "using command: %s", image_path, " ".join(backup_cmd))
            dump_process = subprocess.Popen(backup_cmd,
                                            stderr=subprocess.PIPE,
                                            universal_newlines=True)
            _, err = dump_process.communicate()
            if dump_process.returncode:
                raise exceptions.GadgetError(
                    "mysqldump exited with error code '{0}' and message: "
                    "'{1}'. ".format(dump_process.returncode, err.strip()))
            else:
                _LOGGER.info("Dumping to file was successful.")

        finally:
            # disable global read_lock
            _LOGGER.debug("Unlocking global read lock on source server.")
            source_server.toggle_global_read_lock(False)
            _LOGGER.debug("Source server unlocked. (read-only=OFF)")
            # delete created configuration file
            try:
                _LOGGER.debug("Removing configuration file '%s'",
                              dump_config_file)
                os.remove(dump_config_file)
            except OSError:
                _LOGGER.warning("Unable to remove configuration file '%s'",
                                dump_config_file)
            else:
                _LOGGER.debug("Configuration file '%s' successfully removed",
                              dump_config_file)

        _LOGGER.info(
            "Source server contents successfully cloned to file "
            "'%s'.", image_path)