Files
simple_backup/backup.py
Wim Pomp 6184d6d30a - fix determination of which to keep
- verbose is more verbose
2021-03-29 11:36:48 +02:00

110 lines
3.3 KiB
Python
Executable File

#!/usr/bin/python3
from datetime import date, timedelta, datetime
from glob import iglob
from re import match
import os
from shutil import rmtree
from shlex import split
from subprocess import call
from argparse import ArgumentParser
def dates(sdate):
# sdate: "20180508"
sdate = date(int(sdate[:4]), int(sdate[4:6]), int(sdate[6:]))
edate = datetime.now()
edate = date(edate.year, edate.month, edate.day)
return [(sdate + timedelta(i)) for i in range(int((edate - sdate).days))]
def days(d, n=7):
return d[-n:]
def weeks(d, n=4):
l = {}
for i in d[-7*n:]:
l[(i.year, i.isocalendar()[1])] = i
return sorted(l.values())[-n:]
def months(d, n=12):
l = {}
for i in d[-31*n:]:
l[(i.year, i.month)] = i
return sorted(l.values())[-n:]
def years(d, n=10):
l = {}
for i in d[-366*n:]:
l[i.year] = i
return sorted(l.values())[-n:]
if __name__ == '__main__':
parser = ArgumentParser(description='Backup files.')
parser.add_argument('source', help='source path')
parser.add_argument('dest', help='destination path')
parser.add_argument('-r', '--rsync', help='arguments for rsync', default='-a')
parser.add_argument('-v', '--verbose', help='verbose', action='store_true')
parser.add_argument('-d', '--dry-run', help='dry-run', action='store_true')
parser.add_argument('-l', '--latest', help='name of latest backup', default='latest')
args = parser.parse_args()
run = not args.dry_run
verbose = args.verbose or not run
if not run:
print('Dry-run: do not actually change anything.')
backup_path_full = os.path.join(args.dest, datetime.now().strftime('%Y%m%d-%H%M%S'))
if verbose:
print('Backing up to: {}'.format(backup_path_full))
if run:
os.makedirs(backup_path_full, exist_ok=True)
latest_path = os.path.join(args.dest, args.latest)
if os.path.exists(latest_path):
latest = '--link-dest="{}" '.format(os.path.join(latest_path, args.source[1:], ''))
else:
latest = ''
if run:
os.makedirs(os.path.join(backup_path_full, args.source[1:]))
rsync = 'rsync {} --delete "{}" {} "{}"'.format(args.rsync, os.path.join(args.source, ''), latest,
os.path.join(backup_path_full, args.source[1:]))
if verbose:
print('Running rsync: {}'.format(rsync))
if run:
call(split(rsync))
if os.path.exists(latest_path):
os.remove(latest_path)
os.symlink(backup_path_full, latest_path)
# Deleting old backups
f = [i for i in iglob(os.path.join(args.dest, '*')) if not match('\d{8}-.*', os.path.split(i)[1]) is None]
fn = [os.path.split(i)[1] for i in f]
f = {date(int(j[:4]), int(j[4:6]), int(j[6:8])): i for i, j in zip(f, fn)}
keys = sorted(list(f.keys()))
if verbose:
print('Keeping these backups:')
for fun in (days, weeks, months, years):
print(' {}:'.format(fun.__name__))
for i in fun(keys):
print(' {}'.format(i))
keep = list(set(days(keys) + weeks(keys) + months(keys) + years(keys)))
keep = {f[i] for i in keep}
delete = set(f.values()) - keep
if verbose:
print('Deleting old backups:')
for i in sorted(delete):
print(' {}'.format(i))
if run:
for i in delete:
if os.path.exists(i):
rmtree(i, True)