def _get_capacity_value(self, section, section_name): s = self._required_value(section, section_name, 'Capacity') m = self.CAPA_RE.match(s) if not m: raise BackupError('Configuration file section "{}" has bad ' 'Capacity value "{}": must match /^{}/'.format( section_name, s, self.CAPA_RE.pattern)) return int(s[:-1]) * self.CAPA_SUFFIX_FACTORS[s[-1].lower()]
def rmpolicy_by_name(name): pol = None if name == 'oldest': pol = OldestPolicy() elif name == 'thinning': pol = ThinningPolicy() elif name == 'never': pol = NeverPolicy() else: raise BackupError('Invalid removal policy: "{}"'.format(name)) pol.name = name return pol
def __init__(self, filename): if not os.path.exists(filename): raise BackupError( 'Configuration file {} does not exist'.format(filename)) parser = configparser.ConfigParser() parser['DEFAULT'] = { 'FullBackupsInterval': 'monthly', 'IncrBackupsInterval': 'daily', 'RemovalPolicy': 'thinning', 'IgnoreChangingFiles': False, 'LogsBackupCount': 60 } parser.read(filename) self.instances = [] for section_name, section in parser.items(): if section_name == 'DEFAULT': continue if not section_name.lower().startswith('backup '): raise BackupError( 'Configuration file section name "{}" is ' 'invalid: must begin with "Backup "'.format(section_name)) cfg = _ConfigInstance() cfg.name = section_name[7:] cfg.dest_dir = self._required_value(section, section_name, 'DestinationDir') cfg.dar_args = self._get_dar_args(section, section_name) cfg.capacity = self._get_capacity_value(section, section_name) cfg.full_intvl = schedules.schedule_by_name( section['FullBackupsInterval']) cfg.incr_intvl = schedules.schedule_by_name( section['IncrBackupsInterval']) cfg.rmpolicy = rmpolicies.rmpolicy_by_name( section['RemovalPolicy']) cfg.ignore_changing_files = self._bool_value( section, section_name, 'IgnoreChangingFiles') cfg.logfilename = section.get('LogfileName') cfg.logsbackupcount = int(section.get('LogsBackupCount')) self.instances.append(cfg)
def _bool_value(self, section, section_name, name): value = section[name] if isinstance(value, bool): return value v = value.lower() if v in ('1', 'yes', 'true', 'on'): return True elif v in ('0', 'no', 'false', 'off'): return False else: raise BackupError('Configuration file section "{}" setting "{}" ' 'is not a valid boolean value'.format( section_name, name))
def __init__(self, name, path): self._name = name self._basedir = path archives = [] for fn in os.listdir(path): archive = self._archive_at_path(fn) if archive: logging.debug('Found existing archive {}'.format(fn)) archives.append(archive) archives.sort(key=attrgetter('timestamp')) if len(archives) > 0 and archives[0].is_incremental: raise BackupError('Oldest archive {} is incremental'.format( archive.path())) self._first = None self._last = None self._count = 0 self._total_size = 0 for arc in archives: self._append(arc)
def run(cfg, force_full, force_incr): clean_parts(cfg.dest_dir) now = datetime.datetime.now() arcset = ArchiveSet(cfg.name, cfg.dest_dir) if force_incr: if not arcset: raise BackupError('Cannot run incremental backup: no archives ' 'exist yet') backup(True, cfg, now, arcset) elif force_full or not arcset: backup(False, cfg, now, arcset) else: latest_time = arcset.latest().timestamp if cfg.full_intvl(latest_time, now): backup(False, cfg, now, arcset) elif cfg.incr_intvl(latest_time, now): backup(True, cfg, now, arcset) else: logging.debug('Not time for next backup yet: ' + cfg.name)
def __call__(self, arcset, now): # Don't just return None, because that causes the cleaner to throw a # NoRemovalCandidatesError, which may result in a retry after deleting # the current archive. We don't want that -- we want to give up # immediately. raise BackupError('No more disk space is available')
def _required_value(self, section, section_name, name): if name not in section: raise BackupError('Configuration file section "{}" is missing ' 'required setting "{}"'.format( section_name, name)) return section[name]
def schedule_by_name(name): sched = getattr(Schedules, name, None) if sched: return sched raise BackupError('Invalid backup schedule: "{}"'.format(name))