Example #1
0
    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()
Example #2
0
    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'])
Example #3
0
 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)
Example #4
0
    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'])
Example #5
0
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()
Example #6
0
File: cli.py Project: ozbek/barman
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)
Example #7
0
    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)
Example #8
0
 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))
Example #9
0
 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)
Example #10
0
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()
Example #11
0
 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')
Example #12
0
 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)
Example #13
0
    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()
Example #14
0
    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()
Example #15
0
 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()
Example #16
0
    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()
Example #17
0
    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)
Example #18
0
    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"]),
            )
Example #19
0
 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")
Example #20
0
    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)
Example #21
0
 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
Example #22
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
Example #23
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
Example #24
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")
Example #25
0
    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()
Example #26
0
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()
Example #27
0
    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']))
Example #28
0
    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']))
Example #29
0
    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)
Example #30
0
    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)
Example #31
0
                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
Example #32
0
    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')
Example #33
0
    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
Example #34
0
                        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
Example #35
0
 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']))
Example #36
0
    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'])