def status(self): """ This function show the server status """ # get number of backups no_backups = len( self.get_available_backups(status_filter=(BackupInfo.DONE, ))) output.result('status', self.config.name, "backups_number", "No. of available backups", no_backups) output.result('status', self.config.name, "first_backup", "First available backup", self.get_first_backup_id()) output.result('status', self.config.name, "last_backup", "Last available backup", self.get_last_backup_id()) # Minimum redundancy check. if number of backups minor than minimum # redundancy, fail. if no_backups < self.config.minimum_redundancy: output.result( 'status', self.config.name, "minimum_redundancy", "Minimum redundancy requirements", "FAILED (%s/%s)" % (no_backups, self.config.minimum_redundancy)) else: output.result( 'status', self.config.name, "minimum_redundancy", "Minimum redundancy requirements", "satisfied (%s/%s)" % (no_backups, self.config.minimum_redundancy)) # Output additional status defined by the BackupExecutor if self.executor: self.executor.status()
def recover(self, backup_info, dest, tablespaces=None, target_tli=None, target_time=None, target_xid=None, target_name=None, exclusive=False, remote_command=None): """ Performs a recovery of a backup :param barman.infofile.BackupInfo backup_info: the backup to recover :param str dest: the destination directory :param dict[str,str]|None tablespaces: a tablespace name -> location map (for relocation) :param str|None target_tli: the target timeline :param str|None target_time: the target time :param str|None target_xid: the target xid :param str|None target_name: the target name created previously with pg_create_restore_point() function call :param bool exclusive: whether the recovery is exclusive or not :param str|None remote_command: default None. The remote command to recover the base backup, in case of remote backup. """ # Archive every WAL files in the incoming directory of the server self.server.archive_wal(verbose=False) # Delegate the recovery operation to a RecoveryExecutor object executor = RecoveryExecutor(self) recovery_info = executor.recover(backup_info, dest, tablespaces, target_tli, target_time, target_xid, target_name, exclusive, remote_command) # Output recovery results output.result('recovery', recovery_info['results'])
def show(self): """ Shows the server configuration """ # Populate result map with all the required keys result = dict([ (key, getattr(self.config, key)) for key in self.config.KEYS ]) remote_status = self.get_remote_status() result.update(remote_status) # Backup maximum age section if self.config.last_backup_maximum_age is not None: age = self.backup_manager.validate_last_backup_maximum_age( self.config.last_backup_maximum_age) # If latest backup is between the limits of the # last_backup_maximum_age configuration, display how old is # the latest backup. if age[0]: msg = "%s (latest backup: %s )" % \ (human_readable_timedelta( self.config.last_backup_maximum_age), age[1]) else: # If latest backup is outside the limits of the # last_backup_maximum_age configuration (or the configuration # value is none), warn the user. msg = "%s (WARNING! latest backup is %s old)" % \ (human_readable_timedelta( self.config.last_backup_maximum_age), age[1]) result['last_backup_maximum_age'] = msg else: result['last_backup_maximum_age'] = "None" output.result('show_server', self.config.name, result)
def list_server(minimal=False): """ List available servers, with useful information """ # Get every server, both inactive and temporarily disabled servers = get_server_list() for name in sorted(servers): server = servers[name] # Exception: manage_server_command is not invoked here # Normally you would call manage_server_command to check if the # server is None and to report inactive and disabled servers, but here # we want all servers and the server cannot be None output.init('list_server', name, minimal=minimal) description = server.config.description # If the server has been manually disabled if not server.config.active: description += " (inactive)" # If server has configuration errors elif server.config.disabled: description += " (WARNING: disabled)" # If server is a passive node if server.passive_node: description += ' (Passive)' output.result('list_server', name, description) output.close_and_exit()
def keep(args): """ Tag the specified backup so that it will never be deleted """ if not any((args.release, args.status, args.target)): output.error( "one of the arguments -r/--release -s/--status --target is required" ) output.close_and_exit() server = get_server(args) backup_info = parse_backup_id(server, args) backup_manager = server.backup_manager if args.status: output.init("status", server.config.name) target = backup_manager.get_keep_target(backup_info.backup_id) if target: output.result("status", server.config.name, "keep_status", "Keep", target) else: output.result("status", server.config.name, "keep_status", "Keep", "nokeep") elif args.release: backup_manager.release_keep(backup_info.backup_id) else: if backup_info.status != BackupInfo.DONE: msg = ("Cannot add keep to backup %s because it has status %s. " "Only backups with status DONE can be kept.") % ( backup_info.backup_id, backup_info.status) output.error(msg) output.close_and_exit() backup_manager.keep_backup(backup_info.backup_id, args.target)
def show_backup(self, backup_info): """ Output all available information about a backup :param backup_info: the target backup """ backup_ext_info = self.get_backup_ext_info(backup_info) output.result('show_backup', backup_ext_info)
def check_directories(self): """ Checks backup directories and creates them if they do not exist """ try: self._make_directories() except OSError, e: output.result('check', self.config.name, 'directories', False, "%s: %s" % (e.filename, e.strerror))
def check_retention_policy_settings(self): """ Checks retention policy setting """ if self.config.retention_policy and not self.enforce_retention_policies: output.result('check', self.config.name, 'retention policy settings', False, 'see log') else: output.result('check', self.config.name, 'retention policy settings', True)
def list_server(minimal=False): """ List available servers, with useful information """ servers = get_server_list() for name in sorted(servers): server = servers[name] output.init('list_server', name, minimal=minimal) output.result('list_server', name, server.config.description) output.close_and_exit()
def status(self): # If the PostgreSQL version is < 9.4 pg_stat_archiver is not available. # Retrieve the last_archived_wal using the executor remote_status = self.get_remote_status() if 'last_archived_wal' in remote_status: output.result('status', self.config.name, 'last_archived_wal', 'Last archived WAL', remote_status['last_archived_wal'] or 'No WAL segment shipped yet')
def check(self): """ Checks SSH connection trying to execute a 'true' command on the remote server. """ cmd = Command(self.ssh_command, self.ssh_options) ret = cmd("true") if ret == 0: output.result('check', self.config.name, 'ssh', True) else: output.result('check', self.config.name, 'ssh', False, 'return code: %s' % ret)
def test_result(self): # preparation writer = self._mock_writer() args = ('1', 'two') kwargs = dict(three=3, four=5) output.result('command', *args, **kwargs) output.result('another_command') # writer test writer.result_command.assert_called_once_with(*args, **kwargs) writer.result_another_command.assert_called_once_with()
def status(self): """ Implements the 'server-status' command. """ if self.config.description: output.result('status', self.config.name, "description", "Description", self.config.description) self.status_postgres() self.status_retention_policies() # Executes the backup manager status info method self.backup_manager.status()
def test_result(self): # preparation writer = self._mock_writer() args = ("1", "two") kwargs = dict(three=3, four=5) output.result("command", *args, **kwargs) output.result("another_command") # writer test writer.result_command.assert_called_once_with(*args, **kwargs) writer.result_another_command.assert_called_once_with()
def result(self, server_name, check, status, hint=None): """ Output Strategy constructor :param str server_name: the server being checked :param str check: the check name :param bool status: True if succeeded :param str,None hint: hint to print if not None: """ # Call the basic method super(CheckOutputStrategy, self).result( server_name, check, status, hint) # Send result to output output.result('check', server_name, check, status, hint)
def status(self): """ Set additional status info - invoked by Server.status() """ # We need to get full info here from the server remote_status = self.server.get_remote_status() # If archive_mode is None, there are issues connecting to PostgreSQL if remote_status["archive_mode"] is None: return output.result( "status", self.config.name, "archive_command", "PostgreSQL 'archive_command' setting", remote_status["archive_command"] or "FAILED (please set it accordingly to documentation)", ) last_wal = remote_status.get("last_archived_wal") # If PostgreSQL is >= 9.4 we have the last_archived_time if last_wal and remote_status.get("last_archived_time"): last_wal += ", at %s" % ( remote_status["last_archived_time"].ctime()) output.result( "status", self.config.name, "last_archived_wal", "Last archived WAL", last_wal or "No WAL segment shipped yet", ) # Set output for WAL archive failures (PostgreSQL >= 9.4) if remote_status.get("failed_count") is not None: remote_fail = str(remote_status["failed_count"]) if int(remote_status["failed_count"]) > 0: remote_fail += " (%s at %s)" % ( remote_status["last_failed_wal"], remote_status["last_failed_time"].ctime(), ) output.result( "status", self.config.name, "failed_count", "Failures of WAL archiver", remote_fail, ) # Add hourly archive rate if available (PostgreSQL >= 9.4) and > 0 if remote_status.get("current_archived_wals_per_second"): output.result( "status", self.config.name, "server_archived_wals_per_hour", "Server WAL archiving rate", "%0.2f/hour" % (3600 * remote_status["current_archived_wals_per_second"]), )
def status_retention_policies(self): """ Status of retention policies enforcement """ if self.enforce_retention_policies: output.result('status', self.config.name, "retention_policies", "Retention policies", "enforced " "(mode: %s, retention: %s, WAL retention: %s)" % ( self.config.retention_policy_mode, self.config.retention_policy, self.config.wal_retention_policy)) else: output.result('status', self.config.name, "retention_policies", "Retention policies", "not enforced")
def list_backups(self): """ Lists all the available backups for the server """ retention_status = self.report_backups() backups = self.get_available_backups(BackupInfo.STATUS_ALL) for key in sorted(backups.iterkeys(), reverse=True): backup = backups[key] backup_size = 0 wal_size = 0 rstatus = None if backup.status == BackupInfo.DONE: wal_info = self.get_wal_info(backup) backup_size = backup.size or 0 + wal_info['wal_size'] wal_size = wal_info['wal_until_next_size'] if self.enforce_retention_policies and \ retention_status[backup.backup_id] != BackupInfo.VALID: rstatus = retention_status[backup.backup_id] output.result('list_backup', backup, backup_size, wal_size, rstatus)
def status(self): """ Set additional status info for SshBackupExecutor using remote commands via Ssh, as well as those defined by the given backup strategy. """ # If the PostgreSQL version is < 9.4 pg_stat_archiver is not available. # Retrieve the last_archived_wal using the executor remote_status = self.get_remote_status() if 'last_archived_wal' in remote_status: output.result('status', self.config.name, 'last_archived_wal', 'Last archived WAL', remote_status['last_archived_wal'] or 'No WAL segment shipped yet') try: # Invoke the status() method for the given strategy self.strategy.status() except BaseException: self._update_action_from_strategy() raise
def test_result_bad_command(self, exit_mock, caplog): # preparation writer = self._mock_writer() del writer.result_bad_command output.result('bad_command') # logging test for record in caplog.records: assert record.levelname == 'ERROR' assert 'bad_command' in caplog.text assert 'Traceback' in caplog.text # writer test writer.error_occurred.assert_called_once_with() assert writer.exception.call_count == 1 # exit with error assert exit_mock.called assert exit_mock.call_count == 1 assert exit_mock.call_args[0] != 0
def test_result_bad_command(self, exit_mock, caplog): # preparation writer = self._mock_writer() del writer.result_bad_command output.result('bad_command') # logging test for record in caplog.records(): assert record.levelname == 'ERROR' assert 'bad_command' in caplog.text() assert 'Traceback' in caplog.text() # writer test writer.error_occurred.assert_called_once_with() assert writer.exception.call_count == 1 # exit with error assert exit_mock.called assert exit_mock.call_count == 1 assert exit_mock.call_args[0] != 0
def check_backup_validity(self): """ Check if backup validity requirements are satisfied """ # first check: check backup maximum age if self.config.last_backup_maximum_age is not None: # get maximum age information backup_age = self.backup_manager.validate_last_backup_maximum_age( self.config.last_backup_maximum_age) # format the output output.result('check', self.config.name, 'backup maximum age', backup_age[0], "interval provided: %s, latest backup age: %s" % (human_readable_timedelta( self.config.last_backup_maximum_age), backup_age[1])) else: # last_backup_maximum_age provided by the user output.result('check', self.config.name, 'backup maximum age', True, "no last_backup_maximum_age provided")
def status(self): """ This function show the server status """ # get number of backups no_backups = len(self.get_available_backups()) output.result('status', self.config.name, "backups_number", "No. of available backups", no_backups) output.result('status', self.config.name, "first_backup", "First available backup", self.get_first_backup_id()) output.result('status', self.config.name, "last_backup", "Last available backup", self.get_last_backup_id()) # Minimum redundancy check. if number of backups minor than minimum # redundancy, fail. if no_backups < self.config.minimum_redundancy: output.result('status', self.config.name, "minimum_redundancy", "Minimum redundancy requirements", "FAILED (%s/%s)" % ( no_backups, self.config.minimum_redundancy)) else: output.result('status', self.config.name, "minimum_redundancy", "Minimum redundancy requirements", "satisfied (%s/%s)" % ( no_backups, self.config.minimum_redundancy)) # Output additional status defined by the BackupExecutor if self.executor: self.executor.status()
def list_server(minimal=False): """ List available servers, with useful information """ # Get every server, both inactive and temporarily disabled servers = get_server_list() for name in sorted(servers): server = servers[name] # Exception: manage_server_command is not invoked here # Normally you would call manage_server_command to check if the # server is None and to report inactive and disabled servers, but here # we want all servers and the server cannot be None output.init('list_server', name, minimal=minimal) description = server.config.description # If the server has been manually disabled if not server.config.active: description += " (inactive)" # If server has configuration errors elif server.config.disabled: description += " (WARNING: disabled)" output.result('list_server', name, description) output.close_and_exit()
def status(self): """ Set additional status info - invoked by Server.status() """ remote_status = self.get_remote_status() # If archive_mode is None, there are issues connecting to PostgreSQL if remote_status['archive_mode'] is None: return output.result('status', self.config.name, "archive_command", "PostgreSQL 'archive_command' setting", remote_status['archive_command'] or "FAILED (please set it accordingly to documentation)") last_wal = remote_status.get('last_archived_wal') # If PostgreSQL is >= 9.4 we have the last_archived_time if last_wal and remote_status.get('last_archived_time'): last_wal += ", at %s" % ( remote_status['last_archived_time'].ctime()) output.result('status', self.config.name, "last_archived_wal", "Last archived WAL", last_wal or "No WAL segment shipped yet") # Set output for WAL archive failures (PostgreSQL >= 9.4) if remote_status.get('failed_count') is not None: remote_fail = str(remote_status['failed_count']) if int(remote_status['failed_count']) > 0: remote_fail += " (%s at %s)" % ( remote_status['last_failed_wal'], remote_status['last_failed_time'].ctime()) output.result('status', self.config.name, 'failed_count', 'Failures of WAL archiver', remote_fail) # Add hourly archive rate if available (PostgreSQL >= 9.4) and > 0 if remote_status.get('current_archived_wals_per_second'): output.result( 'status', self.config.name, 'server_archived_wals_per_hour', 'Server WAL archiving rate', '%0.2f/hour' % ( 3600 * remote_status['current_archived_wals_per_second']))
def backup(self): """ Performs a backup for the server """ _logger.debug("initialising backup information") self.executor.init() backup_info = None try: # Create the BackupInfo object representing the backup backup_info = BackupInfo( self.server, backup_id=datetime.datetime.now().strftime('%Y%m%dT%H%M%S')) backup_info.save() self.backup_cache_add(backup_info) output.info( "Starting backup using %s method for server %s in %s", self.mode, self.config.name, backup_info.get_basebackup_directory()) # Run the pre-backup-script if present. script = HookScriptRunner(self, 'backup_script', 'pre') script.env_from_backup_info(backup_info) script.run() # Run the pre-backup-retry-script if present. retry_script = RetryHookScriptRunner( self, 'backup_retry_script', 'pre') retry_script.env_from_backup_info(backup_info) retry_script.run() # Do the backup using the BackupExecutor self.executor.backup(backup_info) # Compute backup size and fsync it on disk self.backup_fsync_and_set_sizes(backup_info) # Mark the backup as DONE backup_info.set_attribute("status", "DONE") # Use BaseException instead of Exception to catch events like # KeyboardInterrupt (e.g.: CRTL-C) except BaseException as e: msg_lines = str(e).strip().splitlines() if backup_info: # Use only the first line of exception message # in backup_info error field backup_info.set_attribute("status", "FAILED") # If the exception has no attached message use the raw # type name if len(msg_lines) == 0: msg_lines = [type(e).__name__] backup_info.set_attribute( "error", "failure %s (%s)" % ( self.executor.current_action, msg_lines[0])) output.error("Backup failed %s.\nDETAILS: %s\n%s", self.executor.current_action, msg_lines[0], '\n'.join(msg_lines[1:])) else: output.info("Backup end at xlog location: %s (%s, %08X)", backup_info.end_xlog, backup_info.end_wal, backup_info.end_offset) output.info("Backup completed") # Create a restore point after a backup target_name = 'barman_%s' % backup_info.backup_id self.server.postgres.create_restore_point(target_name) finally: if backup_info: backup_info.save() # Make sure we are not holding any PostgreSQL connection # during the post-backup scripts self.server.close() # Run the post-backup-retry-script if present. try: retry_script = RetryHookScriptRunner( self, 'backup_retry_script', 'post') retry_script.env_from_backup_info(backup_info) retry_script.run() except AbortedRetryHookScript as e: # Ignore the ABORT_STOP as it is a post-hook operation _logger.warning("Ignoring stop request after receiving " "abort (exit code %d) from post-backup " "retry hook script: %s", e.hook.exit_status, e.hook.script) # Run the post-backup-script if present. script = HookScriptRunner(self, 'backup_script', 'post') script.env_from_backup_info(backup_info) script.run() output.result('backup', backup_info)
def backup(self): """ Performs a backup for the server """ _logger.debug("initialising backup information") self.executor.init() backup_info = None try: # Create the BackupInfo object representing the backup backup_info = BackupInfo( self.server, backup_id=datetime.datetime.now().strftime('%Y%m%dT%H%M%S')) backup_info.save() self.backup_cache_add(backup_info) output.info("Starting backup using %s method for server %s in %s", self.mode, self.config.name, backup_info.get_basebackup_directory()) # Run the pre-backup-script if present. script = HookScriptRunner(self, 'backup_script', 'pre') script.env_from_backup_info(backup_info) script.run() # Run the pre-backup-retry-script if present. retry_script = RetryHookScriptRunner(self, 'backup_retry_script', 'pre') retry_script.env_from_backup_info(backup_info) retry_script.run() # Do the backup using the BackupExecutor self.executor.backup(backup_info) # Compute backup size and fsync it on disk self.backup_fsync_and_set_sizes(backup_info) # Mark the backup as DONE backup_info.set_attribute("status", "DONE") # Use BaseException instead of Exception to catch events like # KeyboardInterrupt (e.g.: CRTL-C) except BaseException as e: msg_lines = str(e).strip().splitlines() if backup_info: # Use only the first line of exception message # in backup_info error field backup_info.set_attribute("status", "FAILED") # If the exception has no attached message use the raw # type name if len(msg_lines) == 0: msg_lines = [type(e).__name__] backup_info.set_attribute( "error", "failure %s (%s)" % (self.executor.current_action, msg_lines[0])) output.error("Backup failed %s.\nDETAILS: %s\n%s", self.executor.current_action, msg_lines[0], '\n'.join(msg_lines[1:])) else: output.info("Backup end at LSN: %s (%s, %08X)", backup_info.end_xlog, backup_info.end_wal, backup_info.end_offset) output.info( "Backup completed (start time: %s, elapsed time: %s)", self.executor.copy_start_time, human_readable_timedelta(self.executor.copy_end_time - self.executor.copy_start_time)) # Create a restore point after a backup target_name = 'barman_%s' % backup_info.backup_id self.server.postgres.create_restore_point(target_name) finally: if backup_info: backup_info.save() # Make sure we are not holding any PostgreSQL connection # during the post-backup scripts self.server.close() # Run the post-backup-retry-script if present. try: retry_script = RetryHookScriptRunner( self, 'backup_retry_script', 'post') retry_script.env_from_backup_info(backup_info) retry_script.run() except AbortedRetryHookScript as e: # Ignore the ABORT_STOP as it is a post-hook operation _logger.warning( "Ignoring stop request after receiving " "abort (exit code %d) from post-backup " "retry hook script: %s", e.hook.exit_status, e.hook.script) # Run the post-backup-script if present. script = HookScriptRunner(self, 'backup_script', 'post') script.env_from_backup_info(backup_info) script.run() output.result('backup', backup_info)
val = getattr(self.config, key) if val is not None and not os.path.isdir(val): #noinspection PyTypeChecker os.makedirs(val) def check_directories(self): """ Checks backup directories and creates them if they do not exist """ try: self._make_directories() except OSError, e: output.result('check', self.config.name, 'directories', False, "%s: %s" % (e.filename, e.strerror)) else: output.result('check', self.config.name, 'directories', True) def check_retention_policy_settings(self): """ Checks retention policy setting """ if self.config.retention_policy and not self.enforce_retention_policies: output.result('check', self.config.name, 'retention policy settings', False, 'see log') else: output.result('check', self.config.name, 'retention policy settings', True) def check_backup_validity(self): """ Check if backup validity requirements are satisfied
def check_postgres(self): """ Checks PostgreSQL connection """ try: remote_status = self.get_remote_status() except PostgresConnectionError: remote_status = None if remote_status is not None and remote_status['server_txt_version']: output.result('check', self.config.name, 'PostgreSQL', True) else: output.result('check', self.config.name, 'PostgreSQL', False) return if BackupOptions.CONCURRENT_BACKUP in self.config.backup_options: if remote_status['pgespresso_installed']: output.result('check', self.config.name, 'pgespresso extension', True) else: output.result('check', self.config.name, 'pgespresso extension', False, 'required for concurrent backups') if remote_status['archive_mode'] == 'on': output.result('check', self.config.name, 'archive_mode', True) else: output.result('check', self.config.name, 'archive_mode', False, "please set it to 'on'") if remote_status['archive_command'] and \ remote_status['archive_command'] != '(disabled)': output.result('check', self.config.name, 'archive_command', True) # Report if the archiving process works without issues. # Skip if the archive_command check fails # It can be None if PostgreSQL is older than 9.4 if remote_status.get('is_archiving') is not None: output.result('check', self.config.name, 'continuous archiving', remote_status['is_archiving']) else: output.result('check', self.config.name, 'archive_command', False, 'please set it accordingly to documentation')
def backup(self, wait=False, wait_timeout=None): """ Performs a backup for the server :param bool wait: wait for all the required WAL files to be archived :param int|None wait_timeout: :return BackupInfo: the generated BackupInfo """ _logger.debug("initialising backup information") self.executor.init() backup_info = None try: # Create the BackupInfo object representing the backup backup_info = LocalBackupInfo( self.server, backup_id=datetime.datetime.now().strftime('%Y%m%dT%H%M%S')) backup_info.set_attribute('systemid', self.server.systemid) backup_info.save() self.backup_cache_add(backup_info) output.info("Starting backup using %s method for server %s in %s", self.mode, self.config.name, backup_info.get_basebackup_directory()) # Run the pre-backup-script if present. script = HookScriptRunner(self, 'backup_script', 'pre') script.env_from_backup_info(backup_info) script.run() # Run the pre-backup-retry-script if present. retry_script = RetryHookScriptRunner(self, 'backup_retry_script', 'pre') retry_script.env_from_backup_info(backup_info) retry_script.run() # Do the backup using the BackupExecutor self.executor.backup(backup_info) # Create a restore point after a backup target_name = 'barman_%s' % backup_info.backup_id self.server.postgres.create_restore_point(target_name) # Free the Postgres connection self.server.postgres.close() # Compute backup size and fsync it on disk self.backup_fsync_and_set_sizes(backup_info) # Mark the backup as WAITING_FOR_WALS backup_info.set_attribute("status", BackupInfo.WAITING_FOR_WALS) # Use BaseException instead of Exception to catch events like # KeyboardInterrupt (e.g.: CTRL-C) except BaseException as e: msg_lines = force_str(e).strip().splitlines() # If the exception has no attached message use the raw # type name if len(msg_lines) == 0: msg_lines = [type(e).__name__] if backup_info: # Use only the first line of exception message # in backup_info error field backup_info.set_attribute("status", BackupInfo.FAILED) backup_info.set_attribute( "error", "failure %s (%s)" % (self.executor.current_action, msg_lines[0])) output.error("Backup failed %s.\nDETAILS: %s", self.executor.current_action, '\n'.join(msg_lines)) else: output.info("Backup end at LSN: %s (%s, %08X)", backup_info.end_xlog, backup_info.end_wal, backup_info.end_offset) executor = self.executor output.info( "Backup completed (start time: %s, elapsed time: %s)", self.executor.copy_start_time, human_readable_timedelta(datetime.datetime.now() - executor.copy_start_time)) # If requested, wait for end_wal to be archived if wait: try: self.server.wait_for_wal(backup_info.end_wal, wait_timeout) self.check_backup(backup_info) except KeyboardInterrupt: # Ignore CTRL-C pressed while waiting for WAL files output.info( "Got CTRL-C. Continuing without waiting for '%s' " "to be archived", backup_info.end_wal) finally: if backup_info: backup_info.save() # Make sure we are not holding any PostgreSQL connection # during the post-backup scripts self.server.close() # Run the post-backup-retry-script if present. try: retry_script = RetryHookScriptRunner( self, 'backup_retry_script', 'post') retry_script.env_from_backup_info(backup_info) retry_script.run() except AbortedRetryHookScript as e: # Ignore the ABORT_STOP as it is a post-hook operation _logger.warning( "Ignoring stop request after receiving " "abort (exit code %d) from post-backup " "retry hook script: %s", e.hook.exit_status, e.hook.script) # Run the post-backup-script if present. script = HookScriptRunner(self, 'backup_script', 'post') script.env_from_backup_info(backup_info) script.run() output.result('backup', backup_info) return backup_info
self, 'backup_retry_script', 'post') retry_script.env_from_backup_info(backup_info) retry_script.run() except AbortedRetryHookScript, e: # Ignore the ABORT_STOP as it is a post-hook operation _logger.warning("Ignoring stop request after receiving " "abort (exit code %d) from post-backup " "retry hook script: %s", e.hook.exit_status, e.hook.script) # Run the post-backup-script if present. script = HookScriptRunner(self, 'backup_script', 'post') script.env_from_backup_info(backup_info) script.run() output.result('backup', backup_info) def recover(self, backup_info, dest, tablespaces=None, target_tli=None, target_time=None, target_xid=None, target_name=None, exclusive=False, remote_command=None): """ Performs a recovery of a backup :param barman.infofile.BackupInfo backup_info: the backup to recover :param str dest: the destination directory :param dict[str,str]|None tablespaces: a tablespace name -> location map (for relocation) :param str|None target_tli: the target timeline :param str|None target_time: the target time :param str|None target_xid: the target xid :param str|None target_name: the target name created previously with
def status_postgres(self): """ Status of PostgreSQL server """ remote_status = self.get_remote_status() if remote_status['server_txt_version']: output.result('status', self.config.name, "pg_version", "PostgreSQL version", remote_status['server_txt_version']) else: output.result('status', self.config.name, "pg_version", "PostgreSQL version", "FAILED trying to get PostgreSQL version") return if remote_status['pgespresso_installed']: output.result('status', self.config.name, 'pgespresso', 'pgespresso extension', "Available") else: output.result('status', self.config.name, 'pgespresso', 'pgespresso extension', "Not available") if remote_status['data_directory']: output.result('status', self.config.name, "data_directory", "PostgreSQL Data directory", remote_status['data_directory']) output.result('status', self.config.name, "archive_command", "PostgreSQL 'archive_command' setting", remote_status['archive_command'] or "FAILED (please set it accordingly to documentation)") last_wal = remote_status.get('last_archived_wal') # If PostgreSQL is >= 9.4 we have the last_archived_time if last_wal and remote_status.get('last_archived_time'): last_wal += ", at %s" % ( remote_status['last_archived_time'].ctime()) output.result('status', self.config.name, "last_archived_wal", "Last archived WAL", last_wal or "No WAL segment shipped yet") if remote_status['current_xlog']: output.result('status', self.config.name, "current_xlog", "Current WAL segment", remote_status['current_xlog']) # Set output for WAL archive failures (PostgreSQL >= 9.4) if remote_status.get('failed_count') is not None: remote_fail = str(remote_status['failed_count']) if int(remote_status['failed_count']) > 0: remote_fail += " (%s at %s)" % ( remote_status['last_failed_wal'], remote_status['last_failed_time'].ctime()) output.result('status', self.config.name, 'failed_count', 'Failures of WAL archiver', remote_fail) # Add hourly archive rate if available (PostgreSQL >= 9.4) and > 0 if remote_status.get('current_archived_wals_per_second'): output.result( 'status', self.config.name, 'server_archived_wals_per_hour', 'Server WAL archiving rate', '%0.2f/hour' % ( 3600 * remote_status['current_archived_wals_per_second']))
def recover(self, backup_info, dest, tablespaces=None, remote_command=None, **kwargs): """ Performs a recovery of a backup :param barman.infofile.LocalBackupInfo backup_info: the backup to recover :param str dest: the destination directory :param dict[str,str]|None tablespaces: a tablespace name -> location map (for relocation) :param str|None remote_command: default None. The remote command to recover the base backup, in case of remote backup. :kwparam str|None target_tli: the target timeline :kwparam str|None target_time: the target time :kwparam str|None target_xid: the target xid :kwparam str|None target_lsn: the target LSN :kwparam str|None target_name: the target name created previously with pg_create_restore_point() function call :kwparam bool|None target_immediate: end recovery as soon as consistency is reached :kwparam bool exclusive: whether the recovery is exclusive or not :kwparam str|None target_action: default None. The recovery target action :kwparam bool|None standby_mode: the standby mode if needed """ # Archive every WAL files in the incoming directory of the server self.server.archive_wal(verbose=False) # Delegate the recovery operation to a RecoveryExecutor object executor = RecoveryExecutor(self) # Run the pre_recovery_script if present. script = HookScriptRunner(self, 'recovery_script', 'pre') script.env_from_recover(backup_info, dest, tablespaces, remote_command, **kwargs) script.run() # Run the pre_recovery_retry_script if present. retry_script = RetryHookScriptRunner(self, 'recovery_retry_script', 'pre') retry_script.env_from_recover(backup_info, dest, tablespaces, remote_command, **kwargs) retry_script.run() # Execute the recovery. # We use a closing context to automatically remove # any resource eventually allocated during recovery. with closing(executor): recovery_info = executor.recover(backup_info, dest, tablespaces=tablespaces, remote_command=remote_command, **kwargs) # Run the post_recovery_retry_script if present. try: retry_script = RetryHookScriptRunner(self, 'recovery_retry_script', 'post') retry_script.env_from_recover(backup_info, dest, tablespaces, remote_command, **kwargs) retry_script.run() except AbortedRetryHookScript as e: # Ignore the ABORT_STOP as it is a post-hook operation _logger.warning( "Ignoring stop request after receiving " "abort (exit code %d) from post-recovery " "retry hook script: %s", e.hook.exit_status, e.hook.script) # Run the post-recovery-script if present. script = HookScriptRunner(self, 'recovery_script', 'post') script.env_from_recover(backup_info, dest, tablespaces, remote_command, **kwargs) script.run() # Output recovery results output.result('recovery', recovery_info['results'])