class Server(object): """ This class represents the PostgreSQL server to backup. """ XLOG_DB = "xlog.db" # the strategy for the management of the results of the various checks __default_check_strategy = CheckOutputStrategy() def __init__(self, config): """ Server constructor. :param barman.config.ServerConfig config: the server configuration """ self.config = config self._conn = None self.server_txt_version = None self.server_version = None if self.config.conninfo is None: raise ConninfoException( 'Missing conninfo parameter in barman configuration ' 'for server %s' % config.name) self.backup_manager = BackupManager(self) self.configuration_files = None self.enforce_retention_policies = False # Set bandwidth_limit if self.config.bandwidth_limit: try: self.config.bandwidth_limit = int(self.config.bandwidth_limit) except ValueError: _logger.warning('Invalid bandwidth_limit "%s" for server "%s" ' '(fallback to "0")' % ( self.config.bandwidth_limit, self.config.name)) self.config.bandwidth_limit = None # set tablespace_bandwidth_limit if self.config.tablespace_bandwidth_limit: rules = {} for rule in self.config.tablespace_bandwidth_limit.split(): try: key, value = rule.split(':', 1) value = int(value) if value != self.config.bandwidth_limit: rules[key] = value except ValueError: _logger.warning( "Invalid tablespace_bandwidth_limit rule '%s'" % rule) if len(rules) > 0: self.config.tablespace_bandwidth_limit = rules else: self.config.tablespace_bandwidth_limit = None # Set minimum redundancy (default 0) if self.config.minimum_redundancy.isdigit(): self.config.minimum_redundancy = int(self.config.minimum_redundancy) if self.config.minimum_redundancy < 0: _logger.warning('Negative value of minimum_redundancy "%s" ' 'for server "%s" (fallback to "0")' % ( self.config.minimum_redundancy, self.config.name)) self.config.minimum_redundancy = 0 else: _logger.warning('Invalid minimum_redundancy "%s" for server "%s" ' '(fallback to "0")' % ( self.config.minimum_redundancy, self.config.name)) self.config.minimum_redundancy = 0 # Initialise retention policies self._init_retention_policies() def _init_retention_policies(self): # Set retention policy mode if self.config.retention_policy_mode != 'auto': _logger.warning( 'Unsupported retention_policy_mode "%s" for server "%s" ' '(fallback to "auto")' % ( self.config.retention_policy_mode, self.config.name)) self.config.retention_policy_mode = 'auto' # If retention_policy is present, enforce them if self.config.retention_policy: # Check wal_retention_policy if self.config.wal_retention_policy != 'main': _logger.warning( 'Unsupported wal_retention_policy value "%s" ' 'for server "%s" (fallback to "main")' % ( self.config.wal_retention_policy, self.config.name)) self.config.wal_retention_policy = 'main' # Create retention policy objects try: rp = RetentionPolicyFactory.create( self, 'retention_policy', self.config.retention_policy) # Reassign the configuration value (we keep it in one place) self.config.retention_policy = rp _logger.debug('Retention policy for server %s: %s' % ( self.config.name, self.config.retention_policy)) try: rp = RetentionPolicyFactory.create( self, 'wal_retention_policy', self.config.wal_retention_policy) # Reassign the configuration value (we keep it in one place) self.config.wal_retention_policy = rp _logger.debug( 'WAL retention policy for server %s: %s' % ( self.config.name, self.config.wal_retention_policy)) except ValueError: _logger.exception( 'Invalid wal_retention_policy setting "%s" ' 'for server "%s" (fallback to "main")' % ( self.config.wal_retention_policy, self.config.name)) rp = RetentionPolicyFactory.create( self, 'wal_retention_policy', 'main') self.config.wal_retention_policy = rp self.enforce_retention_policies = True except ValueError: _logger.exception( 'Invalid retention_policy setting "%s" for server "%s"' % ( self.config.retention_policy, self.config.name)) def check(self, check_strategy=__default_check_strategy): """ Implements the 'server check' command and makes sure SSH and PostgreSQL connections work properly. It checks also that backup directories exist (and if not, it creates them). :param CheckStrategy check_strategy: the strategy for the management of the results of the various checks """ # Check postgres configuration self.check_postgres(check_strategy) # Check barman directories from barman configuration self.check_directories(check_strategy) # Check retention policies self.check_retention_policy_settings(check_strategy) # Check for backup validity self.check_backup_validity(check_strategy) # Executes the backup manager set of checks self.backup_manager.check(check_strategy) def check_postgres(self, check_strategy): """ Checks PostgreSQL connection :param CheckStrategy check_strategy: the strategy for the management of the results of the various checks """ # Take the status of the remote server try: remote_status = self.get_remote_status() except PostgresConnectionError: remote_status = None if remote_status is not None and remote_status['server_txt_version']: check_strategy.result(self.config.name, 'PostgreSQL', True) else: check_strategy.result(self.config.name, 'PostgreSQL', False) return # Check archive_mode parameter: must be on if remote_status['archive_mode'] == 'on': check_strategy.result(self.config.name, 'archive_mode', True) else: check_strategy.result(self.config.name, 'archive_mode', False, "please set it to 'on'") # Check wal_level parameter: must be different to 'minimal' if remote_status['wal_level'] != 'minimal': check_strategy.result( self.config.name, 'wal_level', True) else: check_strategy.result( self.config.name, 'wal_level', False, "please set it to a higher level than 'minimal'") if remote_status['archive_command'] and \ remote_status['archive_command'] != '(disabled)': check_strategy.result(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: check_strategy.result(self.config.name, 'continuous archiving', remote_status['is_archiving']) else: check_strategy.result(self.config.name, 'archive_command', False, 'please set it accordingly to documentation') def _make_directories(self): """ Make backup directories in case they do not exist """ for key in self.config.KEYS: if key.endswith('_directory') and hasattr(self.config, key): 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, check_strategy): """ Checks backup directories and creates them if they do not exist :param CheckStrategy check_strategy: the strategy for the management of the results of the various checks """ if self.config.disabled: check_strategy.result(self.config.name, 'directories', False) for conflict_paths in self.config.msg_list: output.info("\t%s" % conflict_paths) else: try: self._make_directories() except OSError, e: check_strategy.result(self.config.name, 'directories', False, "%s: %s" % (e.filename, e.strerror)) else: