#!/usr/bin/env python # (c) Holger Krekel, hpk@trillke.net, license: GPL2 import py from datetime import datetime from dateutil import parser from dateutil.relativedelta import relativedelta from timeutil import DateRange import time formats = [ "%Y-%m-%d-%H:%M:%S", "%Y-%m-%d-%H:%M", "%Y-%m-%d-%H", "%Y-%m-%d", "%Y-%m", "%d.%m.%Y-%H:%M:%S", "%d.%m.%Y-%H:%M", "%d.%m.%Y", ] def parsedate(s): for format in formats: try: x = py.std.time.strptime(s, format) except ValueError: continue else: return datetime(*x[:6]) raise ValueError, "could not parse %r" % s def unparsedate(dt): return time.strftime(formats[0], dt.timetuple()) class DatedPath(object): def __init__(self, path, date): self.path = py.path.local(path) assert isinstance(date, datetime) self.date = date #if date is None: # x = self.path.basename # i = x.find('-') # if i != -1: # date = parsedate(x[i+1:]) # else: # raise ValueError("could not automatically " # "parse date from %r" % x) #elif isinstance(date, str): # date = parsedate(date) #else: # date = date #self.date = date def __cmp__(self, other): return cmp(self.date, other.date) def __str__(self): return str(self.path) def __repr__(self): return "" % (str(self.date), self.path) # find all backups of a given backupitem # def getdatedpaths(backupitem): if not mountpoint.check(): mountpoint.mkdir() l = mountpoint.listdir(std.path.checker(basestarts=backupitem.name)) n = len(backupitem.name) tuples = [] for x in l: bn = x.basename[n:] try: date = parsedate(bn) except ValueError: continue else: tuples.append((date, x)) return tuples def get_now_dest(backupitem): now = datetime.now() now_string = unparsedate(now) dest = mountpoint.join("%s-%s" %(backupitem.name, now_string)) return dest class kwdict: def __init__(self, **kw): self.__dict__ = kw class TimeSorter: def __init__(self, refdate, datedpaths, days, weeks, months, years): self.refdate = refdate self.datedpaths = datedpaths[:] self.datedpaths.sort() self.spec = kwdict(days=days, weeks=weeks, months=months, years=years) self.remaining = [] self.removable = [] def getintervals(self): intervals = [] for unitname in 'years,months,weeks,days'.split(','): unitvalue = getattr(self.spec, unitname) for i in range(unitvalue-1, -1, -1): relstart = {unitname : i} relend = {unitname : i + 1} start = self.refdate - relativedelta(**relstart) end = self.refdate - relativedelta(**relend) daterange = DateRange(start, end) intervals.append(daterange) return intervals def compute(self): itemneeded = None intervals = self.getintervals() # oldest first #print "spec", self.spec.__dict__ #print "oldest", self.datedpaths[-1].date #for i in intervals: print i #print "intervals" #intervals.sort() #py.std.pprint.pprint(intervals) pathneeded = False used = {} for daterange in intervals: contained = [] for x in self.datedpaths: #print "checking %r in %r" % (x.date, daterange) if x.date in daterange: contained.append(x) if not contained: pathneeded = True else: if pathneeded: # we need to keep the oldest path for traversal # to even older required intervals used[contained[0]] = 1 pathneeded = False # mark youngest as in use used[contained[-1]] = 1 remaining = used.keys() removable = [] for x in self.datedpaths: if x not in remaining: removable.append(x) return remaining, removable def sortout(refdate, datedpaths, days=3, weeks=4, months=3, years=1): """ return (remaining, removable) lists containing the paths that can remain/can be removed according to the spec. """ t = TimeSorter(refdate, datedpaths, days, weeks, months, years) remaining, removable = t.compute() return remaining, removable #debug("rel was %r < %r %s" % (relstart, relend, unitname) ) #debug("check interval %r < %r" %(start, end)) #debug("checking for reduce candidates %r" % (date_paths, )) candidates = [x for x in date_paths if start <= x[0] < end] candidates.sort() while len(candidates) > 1: x,y = candidates.pop(0) assert y.check(dir=1, link=0) # safety belt debug("removing: %s %s" %(x,y)) y.remove(rec=1) if candidates: entry = candidates[0] assert entry[1].check(dir=1, link=0) debug("remaining from %s %s ago: %s %s" % (relend, unitname, entry[0], entry[1]) )