mirror of
https://github.com/psy0rz/zfs_autobackup.git
synced 2025-04-11 22:40:01 +03:00
cleanup old snapshots
This commit is contained in:
parent
24dfb819c3
commit
f4ebd8ac38
@ -17,6 +17,8 @@ parser = argparse.ArgumentParser(description='ZFS autobackup v2.0')
|
|||||||
parser.add_argument('--ssh-source', default="local", help='Source host to get backup from. (user@hostname) Default %(default)s.')
|
parser.add_argument('--ssh-source', default="local", help='Source host to get backup from. (user@hostname) Default %(default)s.')
|
||||||
parser.add_argument('--ssh-target', default="local", help='Target host to push backup to. (user@hostname) Default %(default)s.')
|
parser.add_argument('--ssh-target', default="local", help='Target host to push backup to. (user@hostname) Default %(default)s.')
|
||||||
parser.add_argument('--ssh-cipher', default="arcfour128", help='SSH cipher to use (default %(default)s)')
|
parser.add_argument('--ssh-cipher', default="arcfour128", help='SSH cipher to use (default %(default)s)')
|
||||||
|
parser.add_argument('--keep-source', default=30, help='Number of old snapshots to keep on source. Default %(default)s.')
|
||||||
|
parser.add_argument('--keep-target', default=30, help='Number of old snapshots to keep on target. Default %(default)s.')
|
||||||
parser.add_argument('backup_name', help='Name of the backup (you should set the zfs property "autobackup:backup-name" to true on filesystems you want to backup')
|
parser.add_argument('backup_name', help='Name of the backup (you should set the zfs property "autobackup:backup-name" to true on filesystems you want to backup')
|
||||||
parser.add_argument('target_fs', help='Target filesystem')
|
parser.add_argument('target_fs', help='Target filesystem')
|
||||||
|
|
||||||
@ -45,7 +47,7 @@ def debug(txt):
|
|||||||
|
|
||||||
|
|
||||||
"""run a command. specifiy ssh user@host to run remotely"""
|
"""run a command. specifiy ssh user@host to run remotely"""
|
||||||
def run(cmd, ssh_to=None, tab_split=False, valid_exitcodes=[ 0 ], test=False):
|
def run(cmd, input=None, ssh_to="local", tab_split=False, valid_exitcodes=[ 0 ], test=False):
|
||||||
|
|
||||||
encoded_cmd=[]
|
encoded_cmd=[]
|
||||||
|
|
||||||
@ -59,7 +61,6 @@ def run(cmd, ssh_to=None, tab_split=False, valid_exitcodes=[ 0 ], test=False):
|
|||||||
#(this is neccesary if LC_ALL=en_US.utf8 is not set in the environment)
|
#(this is neccesary if LC_ALL=en_US.utf8 is not set in the environment)
|
||||||
for arg in cmd:
|
for arg in cmd:
|
||||||
encoded_cmd.append(arg.encode('utf-8'))
|
encoded_cmd.append(arg.encode('utf-8'))
|
||||||
|
|
||||||
#the accurate way of displaying it whould be: print encoded_cmd
|
#the accurate way of displaying it whould be: print encoded_cmd
|
||||||
#However, we use the more human-readable way, but this is not always properly escaped!
|
#However, we use the more human-readable way, but this is not always properly escaped!
|
||||||
#(most of the time it should be copypastable however.)
|
#(most of the time it should be copypastable however.)
|
||||||
@ -67,12 +68,20 @@ def run(cmd, ssh_to=None, tab_split=False, valid_exitcodes=[ 0 ], test=False):
|
|||||||
|
|
||||||
if test:
|
if test:
|
||||||
debug("[TEST] "+debug_txt)
|
debug("[TEST] "+debug_txt)
|
||||||
return
|
|
||||||
else:
|
else:
|
||||||
debug(debug_txt)
|
debug(debug_txt)
|
||||||
|
|
||||||
p=subprocess.Popen(encoded_cmd, env=os.environ, stdout=subprocess.PIPE)
|
if input:
|
||||||
output=p.communicate()[0]
|
debug("INPUT:\n"+input.rstrip())
|
||||||
|
stdin=subprocess.PIPE
|
||||||
|
else:
|
||||||
|
stdin=None
|
||||||
|
|
||||||
|
if test:
|
||||||
|
return
|
||||||
|
|
||||||
|
p=subprocess.Popen(encoded_cmd, env=os.environ, stdout=subprocess.PIPE, stdin=stdin)
|
||||||
|
output=p.communicate(input=input)[0]
|
||||||
if p.returncode not in valid_exitcodes:
|
if p.returncode not in valid_exitcodes:
|
||||||
raise(subprocess.CalledProcessError(p.returncode, encoded_cmd))
|
raise(subprocess.CalledProcessError(p.returncode, encoded_cmd))
|
||||||
|
|
||||||
@ -120,6 +129,15 @@ def zfs_get_selected_filesystems(ssh_to, backup_name):
|
|||||||
return(selected_filesystems)
|
return(selected_filesystems)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"""destroy list of filesystems or snapshots"""
|
||||||
|
def zfs_destroy(ssh_to, filesystems):
|
||||||
|
#zfs can only destroy one filesystem at once so we use xargs and stdin
|
||||||
|
run(ssh_to=ssh_to, test=args.test, input="\n".join(filesystems)+"\n", cmd=
|
||||||
|
[ "xargs", "-d", "\\n", "-n", "1", "zfs", "destroy", "-d" ]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
#simulate snapshots for --test option
|
#simulate snapshots for --test option
|
||||||
test_snapshots={}
|
test_snapshots={}
|
||||||
|
|
||||||
@ -253,6 +271,9 @@ if args.test:
|
|||||||
args.verbose=True
|
args.verbose=True
|
||||||
verbose("RUNNING IN TEST-MODE, NOT MAKING ACTUAL BACKUP!")
|
verbose("RUNNING IN TEST-MODE, NOT MAKING ACTUAL BACKUP!")
|
||||||
|
|
||||||
|
if args.keep_source<1 or args.keep_target<1:
|
||||||
|
raise(Exception("Minimum number of snapshots to keep is 1"))
|
||||||
|
|
||||||
#get selected filesystem on backup source
|
#get selected filesystem on backup source
|
||||||
verbose("Getting selected source filesystems for backup {0} on {1}".format(args.backup_name,args.ssh_source))
|
verbose("Getting selected source filesystems for backup {0} on {1}".format(args.backup_name,args.ssh_source))
|
||||||
source_filesystems=zfs_get_selected_filesystems(args.ssh_source, args.backup_name)
|
source_filesystems=zfs_get_selected_filesystems(args.ssh_source, args.backup_name)
|
||||||
@ -318,8 +339,35 @@ for source_filesystem in source_filesystems:
|
|||||||
ssh_source=args.ssh_source, source_filesystem=source_filesystem, first_snapshot=latest_target_snapshot, second_snapshot=send_snapshot,
|
ssh_source=args.ssh_source, source_filesystem=source_filesystem, first_snapshot=latest_target_snapshot, second_snapshot=send_snapshot,
|
||||||
ssh_target=args.ssh_target, target_filesystem=target_filesystem)
|
ssh_target=args.ssh_target, target_filesystem=target_filesystem)
|
||||||
|
|
||||||
|
#update target_snapshot list for later cleanup
|
||||||
|
target_snapshots[target_filesystem].append(send_snapshot)
|
||||||
|
|
||||||
latest_target_snapshot=send_snapshot
|
latest_target_snapshot=send_snapshot
|
||||||
|
|
||||||
|
|
||||||
|
#cleanup old target snapshots
|
||||||
|
target_destroys=[]
|
||||||
|
for target_filesystem in target_snapshots:
|
||||||
|
destroy_count=len(target_snapshots[target_filesystem])-args.keep_target
|
||||||
|
if destroy_count>0:
|
||||||
|
for snapshot in target_snapshots[target_filesystem][0:destroy_count-1]:
|
||||||
|
target_destroys.append(target_filesystem+"@"+snapshot)
|
||||||
|
|
||||||
|
if target_destroys:
|
||||||
|
verbose("Destroying old snapshots on target")
|
||||||
|
zfs_destroy(ssh_to=args.ssh_target, filesystems=target_destroys)
|
||||||
|
|
||||||
|
#cleanup old source snapshots
|
||||||
|
source_destroys=[]
|
||||||
|
for source_filesystem in source_snapshots:
|
||||||
|
destroy_count=len(source_snapshots[source_filesystem])-args.keep_source
|
||||||
|
if destroy_count>0:
|
||||||
|
for snapshot in source_snapshots[source_filesystem][0:destroy_count-1]:
|
||||||
|
source_destroys.append(source_filesystem+"@"+snapshot)
|
||||||
|
|
||||||
|
if source_destroys:
|
||||||
|
verbose("Destroying old snapshots on source")
|
||||||
|
zfs_destroy(ssh_to=args.ssh_source, filesystems=source_destroys)
|
||||||
|
|
||||||
verbose("All done")
|
verbose("All done")
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user