diff --git a/xtrabackup/backup_tools.py b/xtrabackup/backup_tools.py index 0916bc7..4cd0fdc 100644 --- a/xtrabackup/backup_tools.py +++ b/xtrabackup/backup_tools.py @@ -6,7 +6,7 @@ import xtrabackup.exception as exception import xtrabackup.timer as timer import logging - +import datetime class BackupTool: @@ -56,7 +56,7 @@ def prepare_workdir(self, path): def prepare_repository(self, repository, incremental): if incremental: - sub_directory = '/INC' + sub_directory = ''.join(['/INC_', str(self.find_base_index(repository, 'INC'))]) else: sub_directory = '' try: @@ -67,6 +67,21 @@ def prepare_repository(self, repository, incremental): exc_info=self.debug) raise + def find_base_index(self, repository, sub_dir_prefix): + try: + existing_inc_dirs = filesystem_utils.get_prefixed_files_in_dir(repository, ''.join([datetime.datetime.now().strftime("%Y%m%d"), '/', sub_dir_prefix])) + max_base_index = 0 + for existing_inc_dir in existing_inc_dirs: + repo_path, inc_dir = filesystem_utils.split_path(existing_inc_dir) + base_index = int(inc_dir.split('_')[1]) + if base_index > max_base_index: + max_base_index = base_index + return max_base_index + 1 + except exception.ProgramError: + self.logger.error('Unable to find base index.', + exc_info=self.debug) + raise + def prepare_archive_name(self, incremental, incremental_cycle): if incremental: backup_prefix = ''.join(['inc_', str(self.incremental_step), '_']) @@ -196,7 +211,7 @@ def save_incremental_data(self, incremental): def load_incremental_data(self): try: - self.base_dir = filesystem_utils.retrieve_value_from_file( + self.backup_repository = filesystem_utils.retrieve_value_from_file( '/var/tmp/pyxtrabackup-incremental', '^BASEDIR=(.*)$') self.last_lsn = filesystem_utils.retrieve_value_from_file( @@ -231,12 +246,12 @@ def start_incremental_backup(self, repository, incremental, workdir, user, password, threads): self.check_prerequisites(repository) self.prepare_workdir(workdir) - self.prepare_repository(repository, True) if incremental: self.load_incremental_data() self.prepare_archive_name(incremental, True) self.exec_incremental_backup(user, password, threads) else: + self.prepare_repository(repository, True) self.prepare_archive_name(incremental, True) self.exec_full_backup(user, password, threads) self.save_incremental_data(incremental) diff --git a/xtrabackup/filesystem_utils.py b/xtrabackup/filesystem_utils.py index e4f77b3..283d5e9 100644 --- a/xtrabackup/filesystem_utils.py +++ b/xtrabackup/filesystem_utils.py @@ -93,3 +93,8 @@ def split_path(path): def get_prefixed_file_in_dir(directory, prefix): files = glob(''.join([directory, '/', prefix, '*'])) return files[0] + +def get_prefixed_files_in_dir(directory, prefix): + files = glob(''.join([directory, '/', prefix, '*'])) + return files + diff --git a/xtrabackup/restoration.py b/xtrabackup/restoration.py index 4bce256..eb95caa 100644 --- a/xtrabackup/restoration.py +++ b/xtrabackup/restoration.py @@ -11,7 +11,8 @@ [--log-file=] \ [--out-file=] \ [--backup-threads=] \ -[--uncompressed-archives] +[--uncompressed-archives] \ +[--without-stop-mysql] \ pyxtrabackup-restore (-h | --help) pyxtrabackup --version @@ -44,6 +45,10 @@ --uncompressed-archives \ Specify that the backup archives are not compressed. \ Use this option if you did backup with --no-compress. + --without-stop-mysql \ + Does not stop the server for restoration. \ +Ensure that --data-dir is defined to a directory differ from data directory of running MySQL server. + """ from docopt import docopt @@ -62,6 +67,7 @@ def main(): restore_tool.start_restoration(arguments['--base-archive'], arguments['--incremental-archive'], arguments['--tmp-dir'], + arguments['--without-stop-mysql'], arguments['--restart']) except Exception: logger = logging.getLogger(__name__) diff --git a/xtrabackup/restoration_tools.py b/xtrabackup/restoration_tools.py index df395b3..5cfba05 100644 --- a/xtrabackup/restoration_tools.py +++ b/xtrabackup/restoration_tools.py @@ -67,7 +67,7 @@ def restore_incremental_backups(self, incremental_archive): repository, archive_name = filesystem_utils.split_path( incremental_archive) incremental_target = int(archive_name.split('_')[1]) - for step in range(1, incremental_target + 1): + for step in range(0, incremental_target + 1): self.apply_incremental_backup(repository, step) except: self.logger.error( @@ -140,9 +140,10 @@ def clean(self): filesystem_utils.delete_directory_if_exists(self.workdir) def start_restoration(self, base_archive, incremental_archive, - workdir, restart_service): + workdir, without_stop_service, restart_service): self.prepare_workdir(workdir) - self.stop_service() + if not without_stop_service: + self.stop_service() self.clean_data_dir() self.restore_base_backup(base_archive) self.restore_incremental_backups(incremental_archive) @@ -150,4 +151,8 @@ def start_restoration(self, base_archive, incremental_archive, self.set_data_dir_permissions() self.clean() if restart_service: - self.start_service() + if without_stop_service: + self.logger.warning('Both arguments --without-stop-mysql and --restart are used. --restart will be ignored.') + else: + self.start_service() +