summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOskari Timperi <oskari.timperi@iki.fi>2014-04-23 22:00:04 +0300
committerOskari Timperi <oskari.timperi@iki.fi>2014-04-23 22:00:04 +0300
commit45c51aaab49b5efcc87aad3d6b64a410d1caf6b9 (patch)
tree24a1d85abafd11b1e7d9760562b6ab00d37b7be2
downloadtarsnap-backup-master.tar.gz
tarsnap-backup-master.zip
initial commitHEADmaster
-rwxr-xr-xtarsnap-backup.py140
1 files changed, 140 insertions, 0 deletions
diff --git a/tarsnap-backup.py b/tarsnap-backup.py
new file mode 100755
index 0000000..e85617b
--- /dev/null
+++ b/tarsnap-backup.py
@@ -0,0 +1,140 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from datetime import datetime
+import argparse
+import calendar
+import logging
+import os
+import re
+import shlex
+import StringIO
+import subprocess
+import sys
+
+class MyArgumentParser(argparse.ArgumentParser):
+ def __init__(self, *args, **kwargs):
+ super(MyArgumentParser, self).__init__(*args, **kwargs)
+
+ def convert_arg_line_to_args(self, line):
+ for arg in shlex.split(line, comments=True):
+ if not arg.strip():
+ continue
+ yield arg
+
+argparser = MyArgumentParser(fromfile_prefix_chars='@')
+argparser.add_argument('-v', '--verbose', action='count',
+ help='Produce verbose output (can be specified more than once)')
+argparser.add_argument('-n', '--dry-run', action='store_true',
+ help='Do not modify anything')
+argparser.add_argument('-d', '--dir', action='append', required=True,
+ help='Specify a directory to backup (can be given multiple times)')
+argparser.add_argument('--daily', type=int, default=7,
+ help='Number of daily backups to keep (default: 7)')
+argparser.add_argument('--weekly', type=int, default=4,
+ help='Number of weekly backups to keep (default: 4)')
+argparser.add_argument('--monthly', type=int, default=2,
+ help='Number of monthly backups to keep (default: 2)')
+argparser.add_argument('--weekly-day', type=int, default=0,
+ help='Which day to do weekly backups on (0=monday, 6=sunday)')
+argparser.add_argument('--monthly-day', type=int, default=1,
+ help='Which day to do monthly backups on (1-31)')
+
+cmdline_args = argparser.parse_args()
+
+if cmdline_args.verbose == 1:
+ loglevel = logging.INFO
+elif cmdline_args.verbose >= 2:
+ loglevel = logging.DEBUG
+else:
+ loglevel = logging.WARNING
+
+log = logging.getLogger(__name__)
+logging.basicConfig(format="%(asctime)s %(levelname)-8s %(message)s",
+ datefmt="%Y-%m-%d %H:%M:%S", level=loglevel)
+
+now = datetime.now()
+
+backup_name = now.strftime('%Y%m%d-%H%M%S')
+backup_type = None
+
+if now.day == cmdline_args.monthly_day:
+ backup_type = 'monthly'
+elif calendar.weekday(now.year, now.month, now.day) == cmdline_args.weekly_day:
+ backup_type = 'weekly'
+else:
+ backup_type = 'daily'
+
+backup_name += '-{}'.format(backup_type)
+
+log.debug('backup type: %s', backup_type)
+log.debug('backup basename: %s', backup_name)
+
+dirs = set(cmdline_args.dir)
+
+def tarsnap_cmd(*args):
+ cmd = ['tarsnap']
+ cmd += args
+ return cmd
+
+def exec_cmd(cmd, **kwargs):
+ log.debug('executing %s', ' '.join(cmd))
+ subprocess.check_call(cmd, **kwargs)
+
+# create the backups
+for dir in dirs:
+ cmd = tarsnap_cmd('-c', '-f', '{}-{}'.format(backup_name, dir),
+ dir)
+ log.info('backing up %s', dir)
+ if not cmdline_args.dry_run:
+ exec_cmd(cmd)
+
+# retrieve a list of archives and remove old ones
+archives = os.tmpfile()
+
+exec_cmd(tarsnap_cmd('--list-archives'), stdout=archives,
+ stderr=archives)
+
+if cmdline_args.dry_run:
+ for dir in dirs:
+ archives.write('{}-{}\n'.format(backup_name, dir))
+
+delete_archives = []
+
+def get_oldest(lst, keep):
+ return sorted(lst, reverse=True)[keep:]
+
+for dir in dirs:
+ archives.seek(0)
+
+ re_daily = re.compile(r'^\d{8}-\d{6}-daily-' + dir)
+ re_weekly = re.compile(r'^\d{8}-\d{6}-weekly-' + dir)
+ re_monthly = re.compile(r'^\d{8}-\d{6}-monthly-' + dir)
+
+ daily_archives = []
+ weekly_archives = []
+ monthly_archives = []
+
+ for line in archives:
+ line = line.strip()
+ if re_daily.match(line):
+ daily_archives.append(line)
+ elif re_weekly.match(line):
+ weekly_archives.append(line)
+ elif re_monthly.match(line):
+ monthly_archives.append(line)
+
+ delete_archives += get_oldest(daily_archives,
+ cmdline_args.daily)
+ delete_archives += get_oldest(weekly_archives,
+ cmdline_args.weekly)
+ delete_archives += get_oldest(monthly_archives,
+ cmdline_args.monthly)
+
+archives.close()
+
+for archive in delete_archives:
+ log.info('deleting archive %s', archive)
+
+ if not cmdline_args.dry_run:
+ exec_cmd(tarsnap_cmd('-d', '-f', archive))