def interrupt_backup(self): ''' End user interrupts the 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() >>> test_utils.start_fake_backup() >>> backup_task.interrupt_backup() True ''' MAX_SECS = 3 seconds = 0 self.log('interrupting backup') if self.to_backup_dir is not None: # remove all files that suggest this backup is complete bitcoin_utils.delete_last_updated_files(self.to_backup_dir) # add a flag that we started to use this dir to backup self.add_backup_flag() try: bin_dir = os.path.join(virtualenv_dir(), 'bin') args = [ os.path.join(bin_dir, 'killmatch'), constants.BACKUP_PROGRAM ] attempts = 0 while is_backup_running() and attempts < 3: result = command.run(*args).stdout self.log(f'result of stopping backup: {result}') if is_backup_running(): sleep(3) attempts += 1 except CalledProcessError as cpe: self.log(cpe) self.log(format_exc()) # a new page was displayed so give socketio time to connect while seconds < MAX_SECS: self.manager.update_header(self.STOPPED_BACKUP_HEADER) self.manager.update_progress(self.STOPPED_BACKUP_PROGRESS) self.manager.notify_close_window() sleep(1) seconds += 1 # return value is for testing purposes only return not bitcoin_utils.is_backup_running()
def wait_for_backup(self, backup_process): ''' Wait for the backup to finish and display data while waiting. >>> 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.wait_for_backup(backup_process) >>> test_utils.stop_backup() >>> backup_task.wait_for_backup(None) >>> test_utils.stop_backup() ''' def show_line(line): if line is not None and line.startswith('Copying:'): index = line.rfind(os.sep) if index > 0: line = self.COPYING.format(line[index + 1:]) if line != self.COPYING: self.manager.update_progress(line) self.log('starting to wait for backup') if backup_process is None: log_path = os.path.join(BASE_LOG_DIR, whoami(), 'bcb-backup.log') # wait until the log appears while (is_backup_running() and not self.is_interrupted()): if not os.path.exists(log_path): sleep(1) # then display the backup details while (is_backup_running() and not self.is_interrupted()): with open(log_path, 'rt') as backup_log: show_line(backup_log.readline()) else: while (backup_process.poll() is None and not self.is_interrupted()): show_line(backup_process.stdout.readline()) if self.is_interrupted(): self.log('waiting for backup interrupted') else: self.log('finished waiting for backup')
def check_for_conflicts(self, request): ''' Check if there are other apps/tasks running. ''' response = None # check that no other bitcoin-core app is running if bitcoin_utils.is_bitcoin_core_running(): log(f'bitcoind running: {bitcoin_utils.is_bitcoind_running()}') log(f'bitcoin_qt running: {bitcoin_utils.is_bitcoin_qt_running()}') log(f'bitcoin_tx running: {bitcoin_utils.is_bitcoin_tx_running()}') response = warn_core_running(request) # tell user if backup is running elif bitcoin_utils.is_backup_running(): log('backup running') response = warn_bcb_app_running(request, app=constants.BACKUP_PROGRAM) # tell user if restore is already running elif bitcoin_utils.is_restore_running(): log('restore running') response = self.restore() # tell user if another task is running elif accessing_wallet() or updating() or backing_up(): log('task running') response = warn_bcb_task_running(request) return response
def tearDown(self): # make sure nothing is left running test_utils.stop_bitcoin_core_apps() if is_backup_running(): log('interrupting backup') views.InterruptBackup.as_view()(self.factory.get('/bitcoin/interrupt_backup/')) sleep(20) # some tests might create a .bitcon in the home dir test_utils.delete_home_bitcoin_subdir(self.home_bitcoin_subdir_exists)
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 test_backup_when_backups_disabled(self): ''' Test trying to back up when backups disabled. ''' state.set_backups_enabled(False) request = self.factory.get('/bitcoin/backup/') response = views.Backup.as_view()(request) self.assertEqual(response.status_code, 302) self.assertEqual(response.url, '/bitcoin/change_backup_status/') self.assertFalse(is_backup_running()) state.set_backups_enabled(True)
def stop_backup(): ''' Stop the backup. >>> init_database() >>> stop_backup() ''' while bitcoin_utils.is_backup_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.BACKUP_PROGRAM} --exclude {excluded_files}"'] result = command.run(*args).stdout log(f'killing backup result: {result}')
def tearDown(self): # make sure nothing is left running if is_backup_running(): InterruptBackup.as_view()( self.factory.get('/bitcoin/interrupt_backup/')) sleep(20) # make sure bitcoin-qt shuts down; 60 secs may not be enough, # but don't want to waste time waiting needlessly if is_bitcoin_qt_running(): sleep(60) test_utils.stop_bitcoin_qt() # some tests might create a .bitcom in the home dir test_utils.delete_home_bitcoin_subdir(self.home_bitcoin_subdir_exists)
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
def warn_bcb_app_running(request, app=None): ''' Warn that a denova app is running. >>> from django.test import RequestFactory >>> no_backup_message = bytearray( ... NO_BACKUP_IF_CORE_RUNNING, encoding='utf-8') >>> factory = RequestFactory() >>> request = factory.get('/bitcoin/preferences/') >>> response = warn_bcb_app_running(request) >>> response.status_code == 200 True >>> b'Update' in response.content True ''' if app is None: if bitcoin_utils.is_backup_running(): app = constants.BACKUP_PROGRAM else: app = constants.RESTORE_PROGRAM return render(request, 'bitcoin/blockchain_backup_app_running.html', {'app': app})
def get_page(self, request): global update_task log('trying to update blockchain') clear_action_updates() # check that no other bitcoin-core app is running if (bitcoin_utils.is_bitcoin_qt_running() or bitcoin_utils.is_bitcoin_tx_running() or (bitcoin_utils.is_bitcoind_running() and not updating())): response = warn_core_running(request) # tell user if another blockchain_backup app is running elif bitcoin_utils.is_backup_running( ) or bitcoin_utils.is_restore_running(): response = warn_bcb_app_running(request) # tell user if another task is running elif accessing_wallet() or backing_up() or restoring(): response = warn_bcb_task_running(request) else: NOTICE1 = 'WARNING: Don\'t shut down your computer before you <a class="btn btn-secondary " role="button"' NOTICE2 = 'id="stop_button" href="/bitcoin/interrupt_update/" title="Click to stop updating the blockchain">Stop update</a>' NOTICE = f'{NOTICE1} {NOTICE2}' context = bitcoin_utils.get_blockchain_context() data_dir_ok, error = preferences.data_dir_ok() if not preferences.bin_dir_ok(): context['notice'] = BAD_BIN_DIR log(BAD_BIN_DIR) elif not data_dir_ok: context['notice'] = BAD_DATA_DIR log(error) else: context['header'] = "Updating Bitcoin's blockchain" context[ 'notice'] = 'WARNING: Don\'t shut down your computer before you <a class="btn btn-secondary " role="button" id="stop_button" href="/bitcoin/interrupt_update/" title="Click to stop updating the blockchain">Stop update</a>' context[ 'subnotice'] = 'Shutting down before this window says it is safe <em>could damage the blockchain</em>.' context['progress'] = 'Starting to update the blockchain' context['update_interval'] = '5000' if updating(): log('already updating blockchain') else: from blockchain_backup.bitcoin.update import UpdateTask update_task = UpdateTask() update_task.start() log('UpdateTask started') # make sure the button doesn't appear any more bitcoin_utils.send_socketio_message('button', ' ') log(f'context: {context}') response = render(request, 'bitcoin/update.html', context=context) return response
def get_page(self, request): global accessing_wallet_task log('accessing bitcoin interactively') clear_action_updates() # it's ok if bitcoin-qt is running in our task if (bitcoin_utils.is_bitcoind_running() or bitcoin_utils.is_bitcoin_tx_running() or (bitcoin_utils.is_bitcoin_qt_running() and not accessing_wallet())): response = warn_core_running(request) # tell user if another app is running elif bitcoin_utils.is_backup_running( ) or bitcoin_utils.is_restore_running(): response = warn_bcb_app_running(request) # tell user if another task is running elif updating() or backing_up() or restoring(): response = warn_bcb_task_running(request) else: SUBNOTICE1 = "Waiting until you exit Bitcoin-QT.<p>Bitcoin-QT will start in another window." SUBNOTICE2 = "You can send and receive transactions from that window." SUBNOTICE3 = "After you exit Bitcoin-QT, Blockchain Backup will continue updating the blockchain until it's time to back it up." SUBNOTICE4 = "Don't forget to back up your wallet routinely." SUBNOTICE = f'{SUBNOTICE1} {SUBNOTICE2} {SUBNOTICE3} {SUBNOTICE4}' context = bitcoin_utils.get_blockchain_context() data_dir_ok, error = preferences.data_dir_ok() if not preferences.bin_dir_ok(): context['notice'] = BAD_BIN_DIR elif not data_dir_ok: context['notice'] = BAD_DATA_DIR context['subnotice'] = error else: context[ 'header'] = "Accessing Bitcoin Core Wallet Interactively" context[ 'notice'] = 'WARNING: Do not shut down your computer until Bitcoin-QT stops completely.' context['subnotice'] = SUBNOTICE if accessing_wallet(): log('already accessing wallet') else: from blockchain_backup.bitcoin.access_wallet import AccessWalletTask accessing_wallet_task = AccessWalletTask() accessing_wallet_task.start() log('access wallet started') # make sure the button doesn't appear any more bitcoin_utils.send_socketio_message('button', ' ') response = render(request, 'bitcoin/access_wallet.html', context=context) return response