diff --git a/darc/archive.py b/darc/archive.py index c4d1698ac..3707d2f69 100644 --- a/darc/archive.py +++ b/darc/archive.py @@ -43,7 +43,9 @@ class Archive(object): except self.store.DoesNotExist: raise self.DoesNotExist self.metadata = msgpack.unpackb(data) - assert self.metadata['version'] == 1 + if self.metadata['version'] != 1: + raise Exception('Unknown archive metadata version') + self.name = self.metadata['name'] @property def ts(self): diff --git a/darc/archiver.py b/darc/archiver.py index 19dde8dde..2c538135f 100644 --- a/darc/archiver.py +++ b/darc/archiver.py @@ -11,7 +11,7 @@ from .cache import Cache from .key import Key from .helpers import location_validator, format_file_size, format_time,\ format_file_mode, IncludePattern, ExcludePattern, exclude_path, to_localtime, \ - get_cache_dir + get_cache_dir, day_of_year from .remote import StoreServer, RemoteStore class Archiver(object): @@ -224,31 +224,39 @@ class Archiver(object): num_daily = args.daily num_weekly = args.weekly num_monthly = args.monthly - if args.daily + args.weekly + args.monthly == 0: - self.print_error('At least one of the "daily", "weekly", "monthly" ' + num_yearly = args.yearly + if args.daily + args.weekly + args.monthly + args.yearly == 0: + self.print_error('At least one of the "daily", "weekly", "monthly" or "yearly" ' 'settings must be specified') return 1 t0 = date.today() + timedelta(days=1) # Tomorrow - daily = weekly = monthly = 0 + daily = weekly = monthly = yearly = 0 for archive in archives: + if args.prefix and not archive.name.startswith(args.prefix): + continue t = to_localtime(archive.ts).date() if daily < args.daily and t < t0: daily += 1 self.print_verbose('Archive "%s" is daily archive number %d', - archive.metadata['name'], daily) + archive.name, daily) t0 = t elif weekly < args.weekly and t < t0 and t.weekday() == 1: weekly += 1 self.print_verbose('Archive "%s" is weekly archive number %d', - archive.metadata['name'], weekly) + archive.name, weekly) t0 = t elif monthly < args.monthly and t < t0 and t.day == 1: - num_weekly += 1 + monthly += 1 self.print_verbose('Archive "%s" is monthly archive number %d', - archive.metadata['name'], monthly) + archive.name, monthly) + t0 = t + elif yearly < args.yearly and t < t0 and day_of_year(t) == 1: + yearly += 1 + self.print_verbose('Archive "%s" is yearly archive number %d', + archive.name, yearly) t0 = t else: - self.print_verbose('Purging archive %s', archive.metadata['name']) + self.print_verbose('Purging archive %s', archive.name) if args.really: archive.delete(cache) else: @@ -346,6 +354,10 @@ class Archiver(object): help='Number of daily archives to keep') subparser.add_argument('-m', '--monthly', dest='monthly', type=int, default=0, help='Number of monthly archives to keep') + subparser.add_argument('-y', '--yearly', dest='yearly', type=int, default=0, + help='Number of yearly archives to keep') + subparser.add_argument('-p', '--prefix', dest='prefix', type=str, + help='Only consider archive names starting with this prefix') subparser.add_argument('-r', '--really', dest='really', action='store_true', default=False, help='Actually delete archives') diff --git a/darc/helpers.py b/darc/helpers.py index 0ad9953eb..06a6dfea7 100644 --- a/darc/helpers.py +++ b/darc/helpers.py @@ -12,6 +12,12 @@ import sys import time import urllib + +def day_of_year(d): + """Calculate the "day of year" from a date object""" + return int(d.strftime('%j')) + + # OSX filenames are UTF-8 Only so any non-utf8 filenames are url encoded if sys.platform == 'darwin': def encode_filename(name): @@ -23,6 +29,7 @@ if sys.platform == 'darwin': else: encode_filename = str + class Counter(object): __slots__ = ('v',) @@ -48,6 +55,7 @@ def get_keys_dir(): return os.environ.get('DARC_KEYS_DIR', os.path.join(os.path.expanduser('~'), '.darc', 'keys')) + def get_cache_dir(): """Determine where to store keys and cache""" return os.environ.get('DARC_CACHE_DIR', @@ -69,14 +77,17 @@ def deferrable(f): return f(*args, **kw) return wrapper + def error_callback(res, error, data): if res: raise res + def to_localtime(ts): """Convert datetime object from UTC to local time zone""" return ts - timedelta(seconds=time.altzone) + def read_set(path): """Read set from disk (as int32s) """