bundles/zfs: refactor zfs-auto-snapshot
All checks were successful
kunsi/bundlewrap/pipeline/head This commit looks good
All checks were successful
kunsi/bundlewrap/pipeline/head This commit looks good
This commit is contained in:
parent
dab6065b89
commit
cd1a33ccbb
1 changed files with 52 additions and 26 deletions
|
@ -9,21 +9,42 @@ from subprocess import check_call, check_output
|
||||||
from sys import argv
|
from sys import argv
|
||||||
|
|
||||||
|
|
||||||
def create_snap_and_rotate(ds, label, retain, now, all_snapshots):
|
def list_datasets():
|
||||||
new_snap = '{}@zfs-auto-snap_{}-{}'.format(ds, label, now)
|
datasets = set()
|
||||||
check_call(['zfs', 'snapshot', new_snap])
|
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)
|
||||||
|
|
||||||
|
return datasets
|
||||||
|
|
||||||
|
|
||||||
|
def get_filtered_snapshots_for_dataset(ds):
|
||||||
|
all_snapshots = check_output(['zfs', 'list', '-H', '-o', 'name', '-t', 'snapshot', ds]).decode('UTF-8').splitlines()
|
||||||
|
|
||||||
prefix = '{}@zfs-auto-snap_{}-'.format(ds, label)
|
prefix = '{}@zfs-auto-snap_{}-'.format(ds, label)
|
||||||
my_candidates = []
|
snapshots = set()
|
||||||
|
|
||||||
for i in sorted(all_snapshots):
|
for i in sorted(all_snapshots):
|
||||||
if i.startswith(prefix):
|
if i.startswith(prefix):
|
||||||
my_candidates.append(i)
|
snapshots.add(i)
|
||||||
|
|
||||||
my_candidates.append(new_snap)
|
return snapshots
|
||||||
|
|
||||||
for i in my_candidates[:-retain]:
|
|
||||||
assert '@' in i, 'BUG! Dataset "{}" has no @!'.format(i)
|
def delete_snapshot(snap):
|
||||||
check_call(['zfs', 'destroy', i])
|
assert '@' in snap, 'BUG! Dataset "{}" has no @!'.format(snap)
|
||||||
|
print('deleting snapshot {}'.format(snap))
|
||||||
|
check_call(['zfs', 'destroy', snap])
|
||||||
|
|
||||||
|
|
||||||
|
def create_snapshot(ds, label, now):
|
||||||
|
check_call(['zfs', 'snapshot', '{}@zfs-auto-snap_{}-{}'.format(ds, label, now)])
|
||||||
|
print('created snapshot {}@zfs-auto-snap_{}-{}'.format(ds, label, now))
|
||||||
|
|
||||||
|
|
||||||
label = argv[1]
|
label = argv[1]
|
||||||
|
@ -31,28 +52,33 @@ label = argv[1]
|
||||||
with open('/etc/zfs-snapshot-config.json', 'r') as fp:
|
with open('/etc/zfs-snapshot-config.json', 'r') as fp:
|
||||||
metadata = loads(fp.read())
|
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]
|
default_retain = metadata['retain_defaults'][label]
|
||||||
now = datetime.now().strftime('%F-%H%M')
|
now = datetime.now().strftime('%F-%H%M')
|
||||||
snapshots_created = False
|
snapshots_created = False
|
||||||
|
|
||||||
if datasets:
|
for ds in list_datasets():
|
||||||
all_snapshots = check_output(['zfs', 'list', '-H', '-o', 'name', '-t', 'snap']).decode('UTF-8').splitlines()
|
retain = int(metadata.get('retain_per_dataset', {}).get(ds, {}).get(label, default_retain))
|
||||||
|
|
||||||
for ds in datasets:
|
if retain > 0:
|
||||||
retain = int(metadata.get('retain_per_dataset', {}).get(ds, {}).get(label, default_retain))
|
create_snapshot(ds, label, now)
|
||||||
if retain > 0:
|
snapshots_created = True
|
||||||
create_snap_and_rotate(ds, label, retain, now, all_snapshots)
|
|
||||||
snapshots_created = True
|
existing_snapshots = get_filtered_snapshots_for_dataset(ds)
|
||||||
|
|
||||||
|
if retain > 0:
|
||||||
|
# Why +1 here? Because we're specifying the amount of hours we want
|
||||||
|
# to go back in time, not the amount of snapshots we want to keep.
|
||||||
|
# Stating '1 month' does mean 'i want to be able to go back atleast
|
||||||
|
# one monthly snapshot', not 'keep one monthly snapshot'. If we only
|
||||||
|
# kept one snapshot, that wouldn't be possible for most of the time,
|
||||||
|
# because the monthly snapshot is actually less than a month old.
|
||||||
|
snapshots_to_keep = retain+1
|
||||||
|
|
||||||
|
for snapshot in sorted(existing_snapshots)[:-snapshots_to_keep]:
|
||||||
|
delete_snapshot(snapshot)
|
||||||
|
else:
|
||||||
|
for snapshot in existing_snapshots:
|
||||||
|
delete_snapshot(snapshot)
|
||||||
|
|
||||||
with open('/var/tmp/zfs-auto-snapshot.status', 'w') as fp:
|
with open('/var/tmp/zfs-auto-snapshot.status', 'w') as fp:
|
||||||
fp.write('{}\n'.format(datetime.now().strftime('%s') if snapshots_created else 0))
|
fp.write('{}\n'.format(datetime.now().strftime('%s') if snapshots_created else 0))
|
||||||
|
|
Loading…
Reference in a new issue