def start_restore(self): ''' Start restoring the files and directories. >>> from blockchain_backup.bitcoin.tests import utils as test_utils >>> test_utils.init_database() >>> restore_task = RestoreTask(os.path.join(gettempdir(), 'bitcoin/data/testnet3/backups/level1')) >>> restore_task.manager = BitcoinManager(restore_task.log_name) >>> restore_process = restore_task.start_restore() >>> restore_process is not None True >>> test_utils.stop_restore() ''' # do NOT use the --delete flag -- the backups dir itself would be deleted args = [] # restore program is a link to safecopy bin_dir = os.path.join(virtualenv_dir(), 'bin') args.append(os.path.join(bin_dir, constants.RESTORE_PROGRAM)) args.append('--exclude') args.append(bitcoin_utils.get_excluded_files()) args.append('--verbose') args.append('--quick') args.append(f'{self.restore_dir}/*') args.append(self.manager.data_dir) restore_process = Popen(args, stdout=PIPE, universal_newlines=True) return restore_process
def delete_extra_files(self, from_dir, to_dir): ''' Delete files that are not part of the current blockchain. >>> from blockchain_backup.bitcoin.tests import utils as test_utils >>> test_utils.init_database() >>> from_dir = os.path.join(gettempdir(), 'bitcoin/data/testnet3/backups/level1') >>> restore_task = RestoreTask(from_dir) >>> restore_task.manager = BitcoinManager(restore_task.log_name) >>> restore_task.delete_extra_files(from_dir, restore_task.manager.data_dir) True ''' BACKUP_SUBDIR = bitcoin_utils.get_backup_subdir() EXCLUDED_FILES = bitcoin_utils.get_excluded_files() ok = True try: to_entries = os.scandir(to_dir) for entry in to_entries: if entry.name in [EXCLUDED_FILES]: self.log(f'skipping {entry.name}') elif entry.name.startswith(constants.LAST_UPDATED_PREFIX): os.remove(entry.path) self.log(f'deleted {entry.path}') elif entry.is_file(): from_file = os.path.join(from_dir, entry.name) if not os.path.exists(from_file): os.remove(entry.path) self.log(f'deleted {entry.path}') elif entry.is_dir() and entry.name != BACKUP_SUBDIR: from_file = os.path.join(from_dir, entry.name) if os.path.exists(from_file): ok = self.delete_extra_files( os.path.join(from_dir, entry.name), entry.path) else: rmtree(entry.path) self.log(f'deleted dir tree: {entry.path}') if not ok or self.is_interrupted(): break except: # 'bare except' because it catches more than "except Exception" ok = False self.log(format_exc()) if not ok and not self.is_interrupted(): self.manager.update_progress(self.RESTORE_ERROR) return ok
def stop_backup(self, backup_process, backup_pid): ''' Stop backup. >>> from blockchain_backup.bitcoin.tests import utils as test_utils >>> test_utils.init_database() >>> backup_task = BackupTask() >>> backup_task.manager = BitcoinManager(backup_task.log_name) >>> backup_task.prep_backup() >>> backup_process, backup_pid = backup_task.start_backup() >>> backup_process is not None True >>> backup_pid is None True >>> backup_task.stop_backup(backup_process, backup_pid) >>> test_utils.start_fake_backup() >>> backup_pid = get_pid(constants.BACKUP_PROGRAM) >>> backup_task.stop_backup(None, backup_pid) ''' try: if backup_process is None and backup_pid is not None: if is_backup_running(): bin_dir = os.path.join(virtualenv_dir(), 'bin') args = [ os.path.join(bin_dir, 'killmatch'), '"{} --exclude {}"'.format(constants.BACKUP_PROGRAM, get_excluded_files()) ] result = command.run(*args).stdout self.log(f'killing backup result: {result}') try: pid, returncode = os.waitpid(backup_pid, os.P_WAIT) self.log(f'waitpid {pid} return code: {returncode}') except ChildProcessError: self.log('backup_pid already dead') else: # if bcb-backup hasn't stopped yet, then kill it if backup_process is None: self.log('not back process active') else: if backup_process.poll() is None: self.log('killing backup') backup_process.terminate() # wait until backup terminates backup_process.wait() self.log( f'backup return code: {backup_process.returncode}') except: # 'bare except' because it catches more than "except Exception" self.log(f'error while stopping backup\n{format_exc()}')
def stop_restore(): ''' Stop the restore. >>> init_database() >>> stop_restore() ''' while bitcoin_utils.is_restore_running(): sleep(5) bin_dir = os.path.join(virtualenv_dir(), 'bin') excluded_files = bitcoin_utils.get_excluded_files() args = [os.path.join(bin_dir, 'killmatch'), f'"{constants.RESTORE_PROGRAM} --exclude {excluded_files}"'] result = command.run(*args).stdout log(f'killing restore result: {result}')
def stop_restore(self, restore_process, restore_pid): ''' Stop restore. >>> from blockchain_backup.bitcoin.tests import utils as test_utils >>> test_utils.init_database() >>> restore_task = RestoreTask(os.path.join(gettempdir(), 'bitcoin/data/testnet3/backups/level1')) >>> restore_task.manager = BitcoinManager(restore_task.log_name) >>> restore_process = restore_task.start_restore() >>> restore_process is not None True >>> restore_task.stop_restore(restore_process, None) >>> test_utils.start_fake_restore() >>> restore_pid = get_pid(constants.RESTORE_PROGRAM) >>> restore_task.stop_restore(None, restore_pid) ''' try: if restore_process is None: if bitcoin_utils.is_restore_running(): bin_dir = os.path.join(virtualenv_dir(), 'bin') args = [ os.path.join(bin_dir, 'killmatch'), '"{} --exclude {}"'.format( constants.RESTORE_PROGRAM, bitcoin_utils.get_excluded_files()) ] result = command.run(*args).stdout self.log(f'killing restore result: {result}') try: pid, returncode = os.waitpid(restore_pid, os.P_WAIT) self.log(f'waitpid {pid} return code: {returncode}') except ChildProcessError: self.log('restore_pid already dead') else: # if bcb-restore hasn't stopped yet, then kill it if restore_process.poll() is None: self.log('killing restore') restore_process.terminate() # wait until restore terminates restore_process.wait() self.log(f'restore return code: {restore_process.returncode}') except: # 'bare except' because it catches more than "except Exception" self.log(f'error while stopping restore\n{format_exc()}') self.log(f'error while stopping restore\n{format_exc()}')
def start_backup(self): ''' Start backup. >>> from blockchain_backup.bitcoin.tests import utils as test_utils >>> test_utils.init_database() >>> backup_task = BackupTask() >>> backup_task.manager = BitcoinManager(backup_task.log_name) >>> backup_task.prep_backup() >>> backup_process, backup_pid = backup_task.start_backup() >>> backup_process is not None True >>> backup_pid is None True >>> test_utils.stop_backup() ''' bin_dir = os.path.join(virtualenv_dir(), 'bin') data_dir = self.manager.data_dir if not data_dir.endswith(os.sep): data_dir += os.sep if is_backup_running(): backup_process = None backup_pid = get_pid(constants.BACKUP_PROGRAM) self.log('{} is already running using pid: {}'.format( constants.BACKUP_PROGRAM, backup_pid)) else: backup_pid = None args = [] # "bcb-backup" is a link to safecopy so we can distinguish it when we kill it args.append(os.path.join(bin_dir, constants.BACKUP_PROGRAM)) args.append('--exclude') args.append(get_excluded_files()) args.append('--verbose') args.append('--quick') args.append('--delete') args.append(f'{data_dir}*') args.append(self.to_backup_dir) # Popen appears to report "'list' object has no attribute 'split'" # the docs state Popen should pass a sequence as the first arg backup_process = Popen(args, stdout=PIPE, universal_newlines=True) return backup_process, backup_pid