#!/usr/bin/env python3 import re from datetime import datetime from json import loads from subprocess import check_call, check_output from sys import argv def create_snap_and_rotate(ds, label, retain, now, all_snapshots): new_snap = '{}@zfs-auto-snap_{}-{}'.format(ds, label, now) check_call(['zfs', 'snapshot', new_snap]) prefix = '{}@zfs-auto-snap_{}-'.format(ds, label) my_candidates = [] for i in sorted(all_snapshots): if i.startswith(prefix): my_candidates.append(i) my_candidates.append(new_snap) for i in my_candidates[:-retain]: assert '@' in i, 'BUG! Dataset "{}" has no @!'.format(i) check_call(['zfs', 'destroy', i]) label = argv[1] with open('/etc/zfs-snapshot-config.json', 'r') as fp: metadata = loads(fp.read()) datasets = set() for line in check_output(['zfs', 'list', '-H', '-o', 'name']).splitlines(): line = line.decode('UTF-8') for prefix in metadata.get('snapshot_never', set()): if line.startswith(prefix): break else: datasets.add(line) default_retain = metadata['retain_defaults'][label] now = datetime.now().strftime('%F-%H%M') snapshots_created = False if datasets: all_snapshots = check_output(['zfs', 'list', '-H', '-o', 'name', '-t', 'snap']).decode('UTF-8').splitlines() for ds in datasets: retain = int(metadata.get('retain_per_dataset', {}).get(ds, {}).get(label, default_retain)) if retain > 0: create_snap_and_rotate(ds, label, retain, now, all_snapshots) snapshots_created = True with open('/var/tmp/zfs-auto-snapshot.status', 'w') as fp: fp.write('{}\n'.format(datetime.now().strftime('%s') if snapshots_created else 0))